• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/v8.h"
6 
7 #include "src/api.h"
8 #include "src/global-handles.h"
9 
10 #include "src/vm-state-inl.h"
11 
12 namespace v8 {
13 namespace internal {
14 
15 
~ObjectGroup()16 ObjectGroup::~ObjectGroup() {
17   if (info != NULL) info->Dispose();
18   delete[] objects;
19 }
20 
21 
~ImplicitRefGroup()22 ImplicitRefGroup::~ImplicitRefGroup() {
23   delete[] children;
24 }
25 
26 
27 class GlobalHandles::Node {
28  public:
29   // State transition diagram:
30   // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
31   enum State {
32     FREE = 0,
33     NORMAL,     // Normal global handle.
34     WEAK,       // Flagged as weak but not yet finalized.
35     PENDING,    // Has been recognized as only reachable by weak handles.
36     NEAR_DEATH  // Callback has informed the handle is near death.
37   };
38 
39   // Maps handle location (slot) to the containing node.
FromLocation(Object ** location)40   static Node* FromLocation(Object** location) {
41     DCHECK(OFFSET_OF(Node, object_) == 0);
42     return reinterpret_cast<Node*>(location);
43   }
44 
Node()45   Node() {
46     DCHECK(OFFSET_OF(Node, class_id_) == Internals::kNodeClassIdOffset);
47     DCHECK(OFFSET_OF(Node, flags_) == Internals::kNodeFlagsOffset);
48     STATIC_ASSERT(static_cast<int>(NodeState::kMask) ==
49                   Internals::kNodeStateMask);
50     STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue);
51     STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue);
52     STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue);
53     STATIC_ASSERT(static_cast<int>(IsIndependent::kShift) ==
54                   Internals::kNodeIsIndependentShift);
55     STATIC_ASSERT(static_cast<int>(IsPartiallyDependent::kShift) ==
56                   Internals::kNodeIsPartiallyDependentShift);
57   }
58 
59 #ifdef ENABLE_HANDLE_ZAPPING
~Node()60   ~Node() {
61     // TODO(1428): if it's a weak handle we should have invoked its callback.
62     // Zap the values for eager trapping.
63     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
64     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
65     index_ = 0;
66     set_independent(false);
67     set_partially_dependent(false);
68     set_in_new_space_list(false);
69     parameter_or_next_free_.next_free = NULL;
70     weak_callback_ = NULL;
71   }
72 #endif
73 
Initialize(int index,Node ** first_free)74   void Initialize(int index, Node** first_free) {
75     index_ = static_cast<uint8_t>(index);
76     DCHECK(static_cast<int>(index_) == index);
77     set_state(FREE);
78     set_in_new_space_list(false);
79     parameter_or_next_free_.next_free = *first_free;
80     *first_free = this;
81   }
82 
Acquire(Object * object)83   void Acquire(Object* object) {
84     DCHECK(state() == FREE);
85     object_ = object;
86     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
87     set_independent(false);
88     set_partially_dependent(false);
89     set_state(NORMAL);
90     parameter_or_next_free_.parameter = NULL;
91     weak_callback_ = NULL;
92     IncreaseBlockUses();
93   }
94 
Release()95   void Release() {
96     DCHECK(state() != FREE);
97     set_state(FREE);
98     // Zap the values for eager trapping.
99     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
100     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
101     set_independent(false);
102     set_partially_dependent(false);
103     weak_callback_ = NULL;
104     DecreaseBlockUses();
105   }
106 
107   // Object slot accessors.
object() const108   Object* object() const { return object_; }
location()109   Object** location() { return &object_; }
handle()110   Handle<Object> handle() { return Handle<Object>(location()); }
111 
112   // Wrapper class ID accessors.
has_wrapper_class_id() const113   bool has_wrapper_class_id() const {
114     return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId;
115   }
116 
wrapper_class_id() const117   uint16_t wrapper_class_id() const { return class_id_; }
118 
119   // State and flag accessors.
120 
state() const121   State state() const {
122     return NodeState::decode(flags_);
123   }
set_state(State state)124   void set_state(State state) {
125     flags_ = NodeState::update(flags_, state);
126   }
127 
is_independent()128   bool is_independent() {
129     return IsIndependent::decode(flags_);
130   }
set_independent(bool v)131   void set_independent(bool v) {
132     flags_ = IsIndependent::update(flags_, v);
133   }
134 
is_partially_dependent()135   bool is_partially_dependent() {
136     return IsPartiallyDependent::decode(flags_);
137   }
set_partially_dependent(bool v)138   void set_partially_dependent(bool v) {
139     flags_ = IsPartiallyDependent::update(flags_, v);
140   }
141 
is_in_new_space_list()142   bool is_in_new_space_list() {
143     return IsInNewSpaceList::decode(flags_);
144   }
set_in_new_space_list(bool v)145   void set_in_new_space_list(bool v) {
146     flags_ = IsInNewSpaceList::update(flags_, v);
147   }
148 
IsNearDeath() const149   bool IsNearDeath() const {
150     // Check for PENDING to ensure correct answer when processing callbacks.
151     return state() == PENDING || state() == NEAR_DEATH;
152   }
153 
IsWeak() const154   bool IsWeak() const { return state() == WEAK; }
155 
IsRetainer() const156   bool IsRetainer() const { return state() != FREE; }
157 
IsStrongRetainer() const158   bool IsStrongRetainer() const { return state() == NORMAL; }
159 
IsWeakRetainer() const160   bool IsWeakRetainer() const {
161     return state() == WEAK || state() == PENDING || state() == NEAR_DEATH;
162   }
163 
MarkPending()164   void MarkPending() {
165     DCHECK(state() == WEAK);
166     set_state(PENDING);
167   }
168 
169   // Independent flag accessors.
MarkIndependent()170   void MarkIndependent() {
171     DCHECK(state() != FREE);
172     set_independent(true);
173   }
174 
MarkPartiallyDependent()175   void MarkPartiallyDependent() {
176     DCHECK(state() != FREE);
177     if (GetGlobalHandles()->isolate()->heap()->InNewSpace(object_)) {
178       set_partially_dependent(true);
179     }
180   }
clear_partially_dependent()181   void clear_partially_dependent() { set_partially_dependent(false); }
182 
183   // Callback accessor.
184   // TODO(svenpanne) Re-enable or nuke later.
185   // WeakReferenceCallback callback() { return callback_; }
186 
187   // Callback parameter accessors.
set_parameter(void * parameter)188   void set_parameter(void* parameter) {
189     DCHECK(state() != FREE);
190     parameter_or_next_free_.parameter = parameter;
191   }
parameter() const192   void* parameter() const {
193     DCHECK(state() != FREE);
194     return parameter_or_next_free_.parameter;
195   }
196 
197   // Accessors for next free node in the free list.
next_free()198   Node* next_free() {
199     DCHECK(state() == FREE);
200     return parameter_or_next_free_.next_free;
201   }
set_next_free(Node * value)202   void set_next_free(Node* value) {
203     DCHECK(state() == FREE);
204     parameter_or_next_free_.next_free = value;
205   }
206 
MakeWeak(void * parameter,WeakCallback weak_callback)207   void MakeWeak(void* parameter, WeakCallback weak_callback) {
208     DCHECK(weak_callback != NULL);
209     DCHECK(state() != FREE);
210     CHECK(object_ != NULL);
211     set_state(WEAK);
212     set_parameter(parameter);
213     weak_callback_ = weak_callback;
214   }
215 
ClearWeakness()216   void* ClearWeakness() {
217     DCHECK(state() != FREE);
218     void* p = parameter();
219     set_state(NORMAL);
220     set_parameter(NULL);
221     return p;
222   }
223 
PostGarbageCollectionProcessing(Isolate * isolate)224   bool PostGarbageCollectionProcessing(Isolate* isolate) {
225     if (state() != Node::PENDING) return false;
226     if (weak_callback_ == NULL) {
227       Release();
228       return false;
229     }
230     void* par = parameter();
231     set_state(NEAR_DEATH);
232     set_parameter(NULL);
233 
234     Object** object = location();
235     {
236       // Check that we are not passing a finalized external string to
237       // the callback.
238       DCHECK(!object_->IsExternalOneByteString() ||
239              ExternalOneByteString::cast(object_)->resource() != NULL);
240       DCHECK(!object_->IsExternalTwoByteString() ||
241              ExternalTwoByteString::cast(object_)->resource() != NULL);
242       // Leaving V8.
243       VMState<EXTERNAL> state(isolate);
244       HandleScope handle_scope(isolate);
245       Handle<Object> handle(*object, isolate);
246       v8::WeakCallbackData<v8::Value, void> data(
247           reinterpret_cast<v8::Isolate*>(isolate),
248           v8::Utils::ToLocal(handle),
249           par);
250       weak_callback_(data);
251     }
252     // Absence of explicit cleanup or revival of weak handle
253     // in most of the cases would lead to memory leak.
254     CHECK(state() != NEAR_DEATH);
255     return true;
256   }
257 
258   inline GlobalHandles* GetGlobalHandles();
259 
260  private:
261   inline NodeBlock* FindBlock();
262   inline void IncreaseBlockUses();
263   inline void DecreaseBlockUses();
264 
265   // Storage for object pointer.
266   // Placed first to avoid offset computation.
267   Object* object_;
268 
269   // Next word stores class_id, index, state, and independent.
270   // Note: the most aligned fields should go first.
271 
272   // Wrapper class ID.
273   uint16_t class_id_;
274 
275   // Index in the containing handle block.
276   uint8_t index_;
277 
278   // This stores three flags (independent, partially_dependent and
279   // in_new_space_list) and a State.
280   class NodeState:            public BitField<State, 0, 4> {};
281   class IsIndependent:        public BitField<bool,  4, 1> {};
282   class IsPartiallyDependent: public BitField<bool,  5, 1> {};
283   class IsInNewSpaceList:     public BitField<bool,  6, 1> {};
284 
285   uint8_t flags_;
286 
287   // Handle specific callback - might be a weak reference in disguise.
288   WeakCallback weak_callback_;
289 
290   // Provided data for callback.  In FREE state, this is used for
291   // the free list link.
292   union {
293     void* parameter;
294     Node* next_free;
295   } parameter_or_next_free_;
296 
297   DISALLOW_COPY_AND_ASSIGN(Node);
298 };
299 
300 
301 class GlobalHandles::NodeBlock {
302  public:
303   static const int kSize = 256;
304 
NodeBlock(GlobalHandles * global_handles,NodeBlock * next)305   explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next)
306       : next_(next),
307         used_nodes_(0),
308         next_used_(NULL),
309         prev_used_(NULL),
310         global_handles_(global_handles) {}
311 
PutNodesOnFreeList(Node ** first_free)312   void PutNodesOnFreeList(Node** first_free) {
313     for (int i = kSize - 1; i >= 0; --i) {
314       nodes_[i].Initialize(i, first_free);
315     }
316   }
317 
node_at(int index)318   Node* node_at(int index) {
319     DCHECK(0 <= index && index < kSize);
320     return &nodes_[index];
321   }
322 
IncreaseUses()323   void IncreaseUses() {
324     DCHECK(used_nodes_ < kSize);
325     if (used_nodes_++ == 0) {
326       NodeBlock* old_first = global_handles_->first_used_block_;
327       global_handles_->first_used_block_ = this;
328       next_used_ = old_first;
329       prev_used_ = NULL;
330       if (old_first == NULL) return;
331       old_first->prev_used_ = this;
332     }
333   }
334 
DecreaseUses()335   void DecreaseUses() {
336     DCHECK(used_nodes_ > 0);
337     if (--used_nodes_ == 0) {
338       if (next_used_ != NULL) next_used_->prev_used_ = prev_used_;
339       if (prev_used_ != NULL) prev_used_->next_used_ = next_used_;
340       if (this == global_handles_->first_used_block_) {
341         global_handles_->first_used_block_ = next_used_;
342       }
343     }
344   }
345 
global_handles()346   GlobalHandles* global_handles() { return global_handles_; }
347 
348   // Next block in the list of all blocks.
next() const349   NodeBlock* next() const { return next_; }
350 
351   // Next/previous block in the list of blocks with used nodes.
next_used() const352   NodeBlock* next_used() const { return next_used_; }
prev_used() const353   NodeBlock* prev_used() const { return prev_used_; }
354 
355  private:
356   Node nodes_[kSize];
357   NodeBlock* const next_;
358   int used_nodes_;
359   NodeBlock* next_used_;
360   NodeBlock* prev_used_;
361   GlobalHandles* global_handles_;
362 };
363 
364 
GetGlobalHandles()365 GlobalHandles* GlobalHandles::Node::GetGlobalHandles() {
366   return FindBlock()->global_handles();
367 }
368 
369 
FindBlock()370 GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() {
371   intptr_t ptr = reinterpret_cast<intptr_t>(this);
372   ptr = ptr - index_ * sizeof(Node);
373   NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr);
374   DCHECK(block->node_at(index_) == this);
375   return block;
376 }
377 
378 
IncreaseBlockUses()379 void GlobalHandles::Node::IncreaseBlockUses() {
380   NodeBlock* node_block = FindBlock();
381   node_block->IncreaseUses();
382   GlobalHandles* global_handles = node_block->global_handles();
383   global_handles->isolate()->counters()->global_handles()->Increment();
384   global_handles->number_of_global_handles_++;
385 }
386 
387 
DecreaseBlockUses()388 void GlobalHandles::Node::DecreaseBlockUses() {
389   NodeBlock* node_block = FindBlock();
390   GlobalHandles* global_handles = node_block->global_handles();
391   parameter_or_next_free_.next_free = global_handles->first_free_;
392   global_handles->first_free_ = this;
393   node_block->DecreaseUses();
394   global_handles->isolate()->counters()->global_handles()->Decrement();
395   global_handles->number_of_global_handles_--;
396 }
397 
398 
399 class GlobalHandles::NodeIterator {
400  public:
NodeIterator(GlobalHandles * global_handles)401   explicit NodeIterator(GlobalHandles* global_handles)
402       : block_(global_handles->first_used_block_),
403         index_(0) {}
404 
done() const405   bool done() const { return block_ == NULL; }
406 
node() const407   Node* node() const {
408     DCHECK(!done());
409     return block_->node_at(index_);
410   }
411 
Advance()412   void Advance() {
413     DCHECK(!done());
414     if (++index_ < NodeBlock::kSize) return;
415     index_ = 0;
416     block_ = block_->next_used();
417   }
418 
419  private:
420   NodeBlock* block_;
421   int index_;
422 
423   DISALLOW_COPY_AND_ASSIGN(NodeIterator);
424 };
425 
426 
GlobalHandles(Isolate * isolate)427 GlobalHandles::GlobalHandles(Isolate* isolate)
428     : isolate_(isolate),
429       number_of_global_handles_(0),
430       first_block_(NULL),
431       first_used_block_(NULL),
432       first_free_(NULL),
433       post_gc_processing_count_(0),
434       object_group_connections_(kObjectGroupConnectionsCapacity) {}
435 
436 
~GlobalHandles()437 GlobalHandles::~GlobalHandles() {
438   NodeBlock* block = first_block_;
439   while (block != NULL) {
440     NodeBlock* tmp = block->next();
441     delete block;
442     block = tmp;
443   }
444   first_block_ = NULL;
445 }
446 
447 
Create(Object * value)448 Handle<Object> GlobalHandles::Create(Object* value) {
449   if (first_free_ == NULL) {
450     first_block_ = new NodeBlock(this, first_block_);
451     first_block_->PutNodesOnFreeList(&first_free_);
452   }
453   DCHECK(first_free_ != NULL);
454   // Take the first node in the free list.
455   Node* result = first_free_;
456   first_free_ = result->next_free();
457   result->Acquire(value);
458   if (isolate_->heap()->InNewSpace(value) &&
459       !result->is_in_new_space_list()) {
460     new_space_nodes_.Add(result);
461     result->set_in_new_space_list(true);
462   }
463   return result->handle();
464 }
465 
466 
CopyGlobal(Object ** location)467 Handle<Object> GlobalHandles::CopyGlobal(Object** location) {
468   DCHECK(location != NULL);
469   return Node::FromLocation(location)->GetGlobalHandles()->Create(*location);
470 }
471 
472 
Destroy(Object ** location)473 void GlobalHandles::Destroy(Object** location) {
474   if (location != NULL) Node::FromLocation(location)->Release();
475 }
476 
477 
MakeWeak(Object ** location,void * parameter,WeakCallback weak_callback)478 void GlobalHandles::MakeWeak(Object** location,
479                              void* parameter,
480                              WeakCallback weak_callback) {
481   Node::FromLocation(location)->MakeWeak(parameter, weak_callback);
482 }
483 
484 
ClearWeakness(Object ** location)485 void* GlobalHandles::ClearWeakness(Object** location) {
486   return Node::FromLocation(location)->ClearWeakness();
487 }
488 
489 
MarkIndependent(Object ** location)490 void GlobalHandles::MarkIndependent(Object** location) {
491   Node::FromLocation(location)->MarkIndependent();
492 }
493 
494 
MarkPartiallyDependent(Object ** location)495 void GlobalHandles::MarkPartiallyDependent(Object** location) {
496   Node::FromLocation(location)->MarkPartiallyDependent();
497 }
498 
499 
IsIndependent(Object ** location)500 bool GlobalHandles::IsIndependent(Object** location) {
501   return Node::FromLocation(location)->is_independent();
502 }
503 
504 
IsNearDeath(Object ** location)505 bool GlobalHandles::IsNearDeath(Object** location) {
506   return Node::FromLocation(location)->IsNearDeath();
507 }
508 
509 
IsWeak(Object ** location)510 bool GlobalHandles::IsWeak(Object** location) {
511   return Node::FromLocation(location)->IsWeak();
512 }
513 
514 
IterateWeakRoots(ObjectVisitor * v)515 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
516   for (NodeIterator it(this); !it.done(); it.Advance()) {
517     if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location());
518   }
519 }
520 
521 
IdentifyWeakHandles(WeakSlotCallback f)522 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
523   for (NodeIterator it(this); !it.done(); it.Advance()) {
524     if (it.node()->IsWeak() && f(it.node()->location())) {
525       it.node()->MarkPending();
526     }
527   }
528 }
529 
530 
IterateNewSpaceStrongAndDependentRoots(ObjectVisitor * v)531 void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
532   for (int i = 0; i < new_space_nodes_.length(); ++i) {
533     Node* node = new_space_nodes_[i];
534     if (node->IsStrongRetainer() ||
535         (node->IsWeakRetainer() && !node->is_independent() &&
536          !node->is_partially_dependent())) {
537         v->VisitPointer(node->location());
538     }
539   }
540 }
541 
542 
IdentifyNewSpaceWeakIndependentHandles(WeakSlotCallbackWithHeap f)543 void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles(
544     WeakSlotCallbackWithHeap f) {
545   for (int i = 0; i < new_space_nodes_.length(); ++i) {
546     Node* node = new_space_nodes_[i];
547     DCHECK(node->is_in_new_space_list());
548     if ((node->is_independent() || node->is_partially_dependent()) &&
549         node->IsWeak() && f(isolate_->heap(), node->location())) {
550       node->MarkPending();
551     }
552   }
553 }
554 
555 
IterateNewSpaceWeakIndependentRoots(ObjectVisitor * v)556 void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
557   for (int i = 0; i < new_space_nodes_.length(); ++i) {
558     Node* node = new_space_nodes_[i];
559     DCHECK(node->is_in_new_space_list());
560     if ((node->is_independent() || node->is_partially_dependent()) &&
561         node->IsWeakRetainer()) {
562       v->VisitPointer(node->location());
563     }
564   }
565 }
566 
567 
IterateObjectGroups(ObjectVisitor * v,WeakSlotCallbackWithHeap can_skip)568 bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
569                                         WeakSlotCallbackWithHeap can_skip) {
570   ComputeObjectGroupsAndImplicitReferences();
571   int last = 0;
572   bool any_group_was_visited = false;
573   for (int i = 0; i < object_groups_.length(); i++) {
574     ObjectGroup* entry = object_groups_.at(i);
575     DCHECK(entry != NULL);
576 
577     Object*** objects = entry->objects;
578     bool group_should_be_visited = false;
579     for (size_t j = 0; j < entry->length; j++) {
580       Object* object = *objects[j];
581       if (object->IsHeapObject()) {
582         if (!can_skip(isolate_->heap(), &object)) {
583           group_should_be_visited = true;
584           break;
585         }
586       }
587     }
588 
589     if (!group_should_be_visited) {
590       object_groups_[last++] = entry;
591       continue;
592     }
593 
594     // An object in the group requires visiting, so iterate over all
595     // objects in the group.
596     for (size_t j = 0; j < entry->length; ++j) {
597       Object* object = *objects[j];
598       if (object->IsHeapObject()) {
599         v->VisitPointer(&object);
600         any_group_was_visited = true;
601       }
602     }
603 
604     // Once the entire group has been iterated over, set the object
605     // group to NULL so it won't be processed again.
606     delete entry;
607     object_groups_.at(i) = NULL;
608   }
609   object_groups_.Rewind(last);
610   return any_group_was_visited;
611 }
612 
613 
PostGarbageCollectionProcessing(GarbageCollector collector)614 int GlobalHandles::PostGarbageCollectionProcessing(
615     GarbageCollector collector) {
616   // Process weak global handle callbacks. This must be done after the
617   // GC is completely done, because the callbacks may invoke arbitrary
618   // API functions.
619   DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
620   const int initial_post_gc_processing_count = ++post_gc_processing_count_;
621   int freed_nodes = 0;
622   if (collector == SCAVENGER) {
623     for (int i = 0; i < new_space_nodes_.length(); ++i) {
624       Node* node = new_space_nodes_[i];
625       DCHECK(node->is_in_new_space_list());
626       if (!node->IsRetainer()) {
627         // Free nodes do not have weak callbacks. Do not use them to compute
628         // the freed_nodes.
629         continue;
630       }
631       // Skip dependent handles. Their weak callbacks might expect to be
632       // called between two global garbage collection callbacks which
633       // are not called for minor collections.
634       if (!node->is_independent() && !node->is_partially_dependent()) {
635         continue;
636       }
637       node->clear_partially_dependent();
638       if (node->PostGarbageCollectionProcessing(isolate_)) {
639         if (initial_post_gc_processing_count != post_gc_processing_count_) {
640           // Weak callback triggered another GC and another round of
641           // PostGarbageCollection processing.  The current node might
642           // have been deleted in that round, so we need to bail out (or
643           // restart the processing).
644           return freed_nodes;
645         }
646       }
647       if (!node->IsRetainer()) {
648         freed_nodes++;
649       }
650     }
651   } else {
652     for (NodeIterator it(this); !it.done(); it.Advance()) {
653       if (!it.node()->IsRetainer()) {
654         // Free nodes do not have weak callbacks. Do not use them to compute
655         // the freed_nodes.
656         continue;
657       }
658       it.node()->clear_partially_dependent();
659       if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
660         if (initial_post_gc_processing_count != post_gc_processing_count_) {
661           // See the comment above.
662           return freed_nodes;
663         }
664       }
665       if (!it.node()->IsRetainer()) {
666         freed_nodes++;
667       }
668     }
669   }
670   // Update the list of new space nodes.
671   int last = 0;
672   for (int i = 0; i < new_space_nodes_.length(); ++i) {
673     Node* node = new_space_nodes_[i];
674     DCHECK(node->is_in_new_space_list());
675     if (node->IsRetainer()) {
676       if (isolate_->heap()->InNewSpace(node->object())) {
677         new_space_nodes_[last++] = node;
678         isolate_->heap()->IncrementNodesCopiedInNewSpace();
679       } else {
680         node->set_in_new_space_list(false);
681         isolate_->heap()->IncrementNodesPromoted();
682       }
683     } else {
684       node->set_in_new_space_list(false);
685       isolate_->heap()->IncrementNodesDiedInNewSpace();
686     }
687   }
688   new_space_nodes_.Rewind(last);
689   return freed_nodes;
690 }
691 
692 
IterateStrongRoots(ObjectVisitor * v)693 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
694   for (NodeIterator it(this); !it.done(); it.Advance()) {
695     if (it.node()->IsStrongRetainer()) {
696       v->VisitPointer(it.node()->location());
697     }
698   }
699 }
700 
701 
IterateAllRoots(ObjectVisitor * v)702 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
703   for (NodeIterator it(this); !it.done(); it.Advance()) {
704     if (it.node()->IsRetainer()) {
705       v->VisitPointer(it.node()->location());
706     }
707   }
708 }
709 
710 
IterateAllRootsWithClassIds(ObjectVisitor * v)711 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
712   for (NodeIterator it(this); !it.done(); it.Advance()) {
713     if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) {
714       v->VisitEmbedderReference(it.node()->location(),
715                                 it.node()->wrapper_class_id());
716     }
717   }
718 }
719 
720 
IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor * v)721 void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
722   for (int i = 0; i < new_space_nodes_.length(); ++i) {
723     Node* node = new_space_nodes_[i];
724     if (node->IsRetainer() && node->has_wrapper_class_id()) {
725       v->VisitEmbedderReference(node->location(),
726                                 node->wrapper_class_id());
727     }
728   }
729 }
730 
731 
NumberOfWeakHandles()732 int GlobalHandles::NumberOfWeakHandles() {
733   int count = 0;
734   for (NodeIterator it(this); !it.done(); it.Advance()) {
735     if (it.node()->IsWeakRetainer()) {
736       count++;
737     }
738   }
739   return count;
740 }
741 
742 
NumberOfGlobalObjectWeakHandles()743 int GlobalHandles::NumberOfGlobalObjectWeakHandles() {
744   int count = 0;
745   for (NodeIterator it(this); !it.done(); it.Advance()) {
746     if (it.node()->IsWeakRetainer() &&
747         it.node()->object()->IsJSGlobalObject()) {
748       count++;
749     }
750   }
751   return count;
752 }
753 
754 
RecordStats(HeapStats * stats)755 void GlobalHandles::RecordStats(HeapStats* stats) {
756   *stats->global_handle_count = 0;
757   *stats->weak_global_handle_count = 0;
758   *stats->pending_global_handle_count = 0;
759   *stats->near_death_global_handle_count = 0;
760   *stats->free_global_handle_count = 0;
761   for (NodeIterator it(this); !it.done(); it.Advance()) {
762     *stats->global_handle_count += 1;
763     if (it.node()->state() == Node::WEAK) {
764       *stats->weak_global_handle_count += 1;
765     } else if (it.node()->state() == Node::PENDING) {
766       *stats->pending_global_handle_count += 1;
767     } else if (it.node()->state() == Node::NEAR_DEATH) {
768       *stats->near_death_global_handle_count += 1;
769     } else if (it.node()->state() == Node::FREE) {
770       *stats->free_global_handle_count += 1;
771     }
772   }
773 }
774 
775 #ifdef DEBUG
776 
PrintStats()777 void GlobalHandles::PrintStats() {
778   int total = 0;
779   int weak = 0;
780   int pending = 0;
781   int near_death = 0;
782   int destroyed = 0;
783 
784   for (NodeIterator it(this); !it.done(); it.Advance()) {
785     total++;
786     if (it.node()->state() == Node::WEAK) weak++;
787     if (it.node()->state() == Node::PENDING) pending++;
788     if (it.node()->state() == Node::NEAR_DEATH) near_death++;
789     if (it.node()->state() == Node::FREE) destroyed++;
790   }
791 
792   PrintF("Global Handle Statistics:\n");
793   PrintF("  allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
794   PrintF("  # weak       = %d\n", weak);
795   PrintF("  # pending    = %d\n", pending);
796   PrintF("  # near_death = %d\n", near_death);
797   PrintF("  # free       = %d\n", destroyed);
798   PrintF("  # total      = %d\n", total);
799 }
800 
801 
Print()802 void GlobalHandles::Print() {
803   PrintF("Global handles:\n");
804   for (NodeIterator it(this); !it.done(); it.Advance()) {
805     PrintF("  handle %p to %p%s\n",
806            reinterpret_cast<void*>(it.node()->location()),
807            reinterpret_cast<void*>(it.node()->object()),
808            it.node()->IsWeak() ? " (weak)" : "");
809   }
810 }
811 
812 #endif
813 
814 
815 
AddObjectGroup(Object *** handles,size_t length,v8::RetainedObjectInfo * info)816 void GlobalHandles::AddObjectGroup(Object*** handles,
817                                    size_t length,
818                                    v8::RetainedObjectInfo* info) {
819 #ifdef DEBUG
820   for (size_t i = 0; i < length; ++i) {
821     DCHECK(!Node::FromLocation(handles[i])->is_independent());
822   }
823 #endif
824   if (length == 0) {
825     if (info != NULL) info->Dispose();
826     return;
827   }
828   ObjectGroup* group = new ObjectGroup(length);
829   for (size_t i = 0; i < length; ++i)
830     group->objects[i] = handles[i];
831   group->info = info;
832   object_groups_.Add(group);
833 }
834 
835 
SetObjectGroupId(Object ** handle,UniqueId id)836 void GlobalHandles::SetObjectGroupId(Object** handle,
837                                      UniqueId id) {
838   object_group_connections_.Add(ObjectGroupConnection(id, handle));
839 }
840 
841 
SetRetainedObjectInfo(UniqueId id,RetainedObjectInfo * info)842 void GlobalHandles::SetRetainedObjectInfo(UniqueId id,
843                                           RetainedObjectInfo* info) {
844   retainer_infos_.Add(ObjectGroupRetainerInfo(id, info));
845 }
846 
847 
SetReferenceFromGroup(UniqueId id,Object ** child)848 void GlobalHandles::SetReferenceFromGroup(UniqueId id, Object** child) {
849   DCHECK(!Node::FromLocation(child)->is_independent());
850   implicit_ref_connections_.Add(ObjectGroupConnection(id, child));
851 }
852 
853 
SetReference(HeapObject ** parent,Object ** child)854 void GlobalHandles::SetReference(HeapObject** parent, Object** child) {
855   DCHECK(!Node::FromLocation(child)->is_independent());
856   ImplicitRefGroup* group = new ImplicitRefGroup(parent, 1);
857   group->children[0] = child;
858   implicit_ref_groups_.Add(group);
859 }
860 
861 
RemoveObjectGroups()862 void GlobalHandles::RemoveObjectGroups() {
863   for (int i = 0; i < object_groups_.length(); i++)
864     delete object_groups_.at(i);
865   object_groups_.Clear();
866   for (int i = 0; i < retainer_infos_.length(); ++i)
867     retainer_infos_[i].info->Dispose();
868   retainer_infos_.Clear();
869   object_group_connections_.Clear();
870   object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
871 }
872 
873 
RemoveImplicitRefGroups()874 void GlobalHandles::RemoveImplicitRefGroups() {
875   for (int i = 0; i < implicit_ref_groups_.length(); i++) {
876     delete implicit_ref_groups_.at(i);
877   }
878   implicit_ref_groups_.Clear();
879   implicit_ref_connections_.Clear();
880 }
881 
882 
TearDown()883 void GlobalHandles::TearDown() {
884   // TODO(1428): invoke weak callbacks.
885 }
886 
887 
ComputeObjectGroupsAndImplicitReferences()888 void GlobalHandles::ComputeObjectGroupsAndImplicitReferences() {
889   if (object_group_connections_.length() == 0) {
890     for (int i = 0; i < retainer_infos_.length(); ++i)
891       retainer_infos_[i].info->Dispose();
892     retainer_infos_.Clear();
893     implicit_ref_connections_.Clear();
894     return;
895   }
896 
897   object_group_connections_.Sort();
898   retainer_infos_.Sort();
899   implicit_ref_connections_.Sort();
900 
901   int info_index = 0;  // For iterating retainer_infos_.
902   UniqueId current_group_id(0);
903   int current_group_start = 0;
904 
905   int current_implicit_refs_start = 0;
906   int current_implicit_refs_end = 0;
907   for (int i = 0; i <= object_group_connections_.length(); ++i) {
908     if (i == 0)
909       current_group_id = object_group_connections_[i].id;
910     if (i == object_group_connections_.length() ||
911         current_group_id != object_group_connections_[i].id) {
912       // Group detected: objects in indices [current_group_start, i[.
913 
914       // Find out which implicit references are related to this group. (We want
915       // to ignore object groups which only have 1 object, but that object is
916       // needed as a representative object for the implicit refrerence group.)
917       while (current_implicit_refs_start < implicit_ref_connections_.length() &&
918              implicit_ref_connections_[current_implicit_refs_start].id <
919                  current_group_id)
920         ++current_implicit_refs_start;
921       current_implicit_refs_end = current_implicit_refs_start;
922       while (current_implicit_refs_end < implicit_ref_connections_.length() &&
923              implicit_ref_connections_[current_implicit_refs_end].id ==
924                  current_group_id)
925         ++current_implicit_refs_end;
926 
927       if (current_implicit_refs_end > current_implicit_refs_start) {
928         // Find a representative object for the implicit references.
929         HeapObject** representative = NULL;
930         for (int j = current_group_start; j < i; ++j) {
931           Object** object = object_group_connections_[j].object;
932           if ((*object)->IsHeapObject()) {
933             representative = reinterpret_cast<HeapObject**>(object);
934             break;
935           }
936         }
937         if (representative) {
938           ImplicitRefGroup* group = new ImplicitRefGroup(
939               representative,
940               current_implicit_refs_end - current_implicit_refs_start);
941           for (int j = current_implicit_refs_start;
942                j < current_implicit_refs_end;
943                ++j) {
944             group->children[j - current_implicit_refs_start] =
945                 implicit_ref_connections_[j].object;
946           }
947           implicit_ref_groups_.Add(group);
948         }
949         current_implicit_refs_start = current_implicit_refs_end;
950       }
951 
952       // Find a RetainedObjectInfo for the group.
953       RetainedObjectInfo* info = NULL;
954       while (info_index < retainer_infos_.length() &&
955              retainer_infos_[info_index].id < current_group_id) {
956         retainer_infos_[info_index].info->Dispose();
957         ++info_index;
958       }
959       if (info_index < retainer_infos_.length() &&
960           retainer_infos_[info_index].id == current_group_id) {
961         // This object group has an associated ObjectGroupRetainerInfo.
962         info = retainer_infos_[info_index].info;
963         ++info_index;
964       }
965 
966       // Ignore groups which only contain one object.
967       if (i > current_group_start + 1) {
968         ObjectGroup* group = new ObjectGroup(i - current_group_start);
969         for (int j = current_group_start; j < i; ++j) {
970           group->objects[j - current_group_start] =
971               object_group_connections_[j].object;
972         }
973         group->info = info;
974         object_groups_.Add(group);
975       } else if (info) {
976         info->Dispose();
977       }
978 
979       if (i < object_group_connections_.length()) {
980         current_group_id = object_group_connections_[i].id;
981         current_group_start = i;
982       }
983     }
984   }
985   object_group_connections_.Clear();
986   object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
987   retainer_infos_.Clear();
988   implicit_ref_connections_.Clear();
989 }
990 
991 
EternalHandles()992 EternalHandles::EternalHandles() : size_(0) {
993   for (unsigned i = 0; i < arraysize(singleton_handles_); i++) {
994     singleton_handles_[i] = kInvalidIndex;
995   }
996 }
997 
998 
~EternalHandles()999 EternalHandles::~EternalHandles() {
1000   for (int i = 0; i < blocks_.length(); i++) delete[] blocks_[i];
1001 }
1002 
1003 
IterateAllRoots(ObjectVisitor * visitor)1004 void EternalHandles::IterateAllRoots(ObjectVisitor* visitor) {
1005   int limit = size_;
1006   for (int i = 0; i < blocks_.length(); i++) {
1007     DCHECK(limit > 0);
1008     Object** block = blocks_[i];
1009     visitor->VisitPointers(block, block + Min(limit, kSize));
1010     limit -= kSize;
1011   }
1012 }
1013 
1014 
IterateNewSpaceRoots(ObjectVisitor * visitor)1015 void EternalHandles::IterateNewSpaceRoots(ObjectVisitor* visitor) {
1016   for (int i = 0; i < new_space_indices_.length(); i++) {
1017     visitor->VisitPointer(GetLocation(new_space_indices_[i]));
1018   }
1019 }
1020 
1021 
PostGarbageCollectionProcessing(Heap * heap)1022 void EternalHandles::PostGarbageCollectionProcessing(Heap* heap) {
1023   int last = 0;
1024   for (int i = 0; i < new_space_indices_.length(); i++) {
1025     int index = new_space_indices_[i];
1026     if (heap->InNewSpace(*GetLocation(index))) {
1027       new_space_indices_[last++] = index;
1028     }
1029   }
1030   new_space_indices_.Rewind(last);
1031 }
1032 
1033 
Create(Isolate * isolate,Object * object,int * index)1034 void EternalHandles::Create(Isolate* isolate, Object* object, int* index) {
1035   DCHECK_EQ(kInvalidIndex, *index);
1036   if (object == NULL) return;
1037   DCHECK_NE(isolate->heap()->the_hole_value(), object);
1038   int block = size_ >> kShift;
1039   int offset = size_ & kMask;
1040   // need to resize
1041   if (offset == 0) {
1042     Object** next_block = new Object*[kSize];
1043     Object* the_hole = isolate->heap()->the_hole_value();
1044     MemsetPointer(next_block, the_hole, kSize);
1045     blocks_.Add(next_block);
1046   }
1047   DCHECK_EQ(isolate->heap()->the_hole_value(), blocks_[block][offset]);
1048   blocks_[block][offset] = object;
1049   if (isolate->heap()->InNewSpace(object)) {
1050     new_space_indices_.Add(size_);
1051   }
1052   *index = size_++;
1053 }
1054 
1055 
1056 } }  // namespace v8::internal
1057