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.h"
8 #include "src/v8.h"
9 #include "src/vm-state-inl.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 
~ObjectGroup()15 ObjectGroup::~ObjectGroup() {
16   if (info != NULL) info->Dispose();
17   delete[] objects;
18 }
19 
20 
~ImplicitRefGroup()21 ImplicitRefGroup::~ImplicitRefGroup() {
22   delete[] children;
23 }
24 
25 
26 class GlobalHandles::Node {
27  public:
28   // State transition diagram:
29   // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
30   enum State {
31     FREE = 0,
32     NORMAL,      // Normal global handle.
33     WEAK,        // Flagged as weak but not yet finalized.
34     PENDING,     // Has been recognized as only reachable by weak handles.
35     NEAR_DEATH,  // Callback has informed the handle is near death.
36     NUMBER_OF_NODE_STATES
37   };
38 
39   // Maps handle location (slot) to the containing node.
FromLocation(Object ** location)40   static Node* FromLocation(Object** location) {
41     DCHECK(offsetof(Node, object_) == 0);
42     return reinterpret_cast<Node*>(location);
43   }
44 
Node()45   Node() {
46     DCHECK(offsetof(Node, class_id_) == Internals::kNodeClassIdOffset);
47     DCHECK(offsetof(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     STATIC_ASSERT(static_cast<int>(IsActive::kShift) ==
58                   Internals::kNodeIsActiveShift);
59   }
60 
61 #ifdef ENABLE_HANDLE_ZAPPING
~Node()62   ~Node() {
63     // TODO(1428): if it's a weak handle we should have invoked its callback.
64     // Zap the values for eager trapping.
65     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
66     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
67     index_ = 0;
68     set_independent(false);
69     if (FLAG_scavenge_reclaim_unmodified_objects) {
70       set_active(false);
71     } else {
72       set_partially_dependent(false);
73     }
74     set_in_new_space_list(false);
75     parameter_or_next_free_.next_free = NULL;
76     weak_callback_ = NULL;
77   }
78 #endif
79 
Initialize(int index,Node ** first_free)80   void Initialize(int index, Node** first_free) {
81     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
82     index_ = static_cast<uint8_t>(index);
83     DCHECK(static_cast<int>(index_) == index);
84     set_state(FREE);
85     set_weakness_type(NORMAL_WEAK);
86     set_in_new_space_list(false);
87     parameter_or_next_free_.next_free = *first_free;
88     *first_free = this;
89   }
90 
Acquire(Object * object)91   void Acquire(Object* object) {
92     DCHECK(state() == FREE);
93     object_ = object;
94     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
95     set_independent(false);
96     if (FLAG_scavenge_reclaim_unmodified_objects) {
97       set_active(false);
98     } else {
99       set_partially_dependent(false);
100     }
101     set_state(NORMAL);
102     parameter_or_next_free_.parameter = NULL;
103     weak_callback_ = NULL;
104     IncreaseBlockUses();
105   }
106 
Zap()107   void Zap() {
108     DCHECK(IsInUse());
109     // Zap the values for eager trapping.
110     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
111   }
112 
Release()113   void Release() {
114     DCHECK(IsInUse());
115     set_state(FREE);
116     // Zap the values for eager trapping.
117     object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
118     class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
119     set_independent(false);
120     if (FLAG_scavenge_reclaim_unmodified_objects) {
121       set_active(false);
122     } else {
123       set_partially_dependent(false);
124     }
125     weak_callback_ = NULL;
126     DecreaseBlockUses();
127   }
128 
129   // Object slot accessors.
object() const130   Object* object() const { return object_; }
location()131   Object** location() { return &object_; }
handle()132   Handle<Object> handle() { return Handle<Object>(location()); }
133 
134   // Wrapper class ID accessors.
has_wrapper_class_id() const135   bool has_wrapper_class_id() const {
136     return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId;
137   }
138 
wrapper_class_id() const139   uint16_t wrapper_class_id() const { return class_id_; }
140 
141   // State and flag accessors.
142 
state() const143   State state() const {
144     return NodeState::decode(flags_);
145   }
set_state(State state)146   void set_state(State state) {
147     flags_ = NodeState::update(flags_, state);
148   }
149 
is_independent()150   bool is_independent() {
151     return IsIndependent::decode(flags_);
152   }
set_independent(bool v)153   void set_independent(bool v) {
154     flags_ = IsIndependent::update(flags_, v);
155   }
156 
is_partially_dependent()157   bool is_partially_dependent() {
158     CHECK(!FLAG_scavenge_reclaim_unmodified_objects);
159     return IsPartiallyDependent::decode(flags_);
160   }
set_partially_dependent(bool v)161   void set_partially_dependent(bool v) {
162     CHECK(!FLAG_scavenge_reclaim_unmodified_objects);
163     flags_ = IsPartiallyDependent::update(flags_, v);
164   }
165 
is_active()166   bool is_active() {
167     CHECK(FLAG_scavenge_reclaim_unmodified_objects);
168     return IsActive::decode(flags_);
169   }
set_active(bool v)170   void set_active(bool v) {
171     CHECK(FLAG_scavenge_reclaim_unmodified_objects);
172     flags_ = IsActive::update(flags_, v);
173   }
174 
is_in_new_space_list()175   bool is_in_new_space_list() {
176     return IsInNewSpaceList::decode(flags_);
177   }
set_in_new_space_list(bool v)178   void set_in_new_space_list(bool v) {
179     flags_ = IsInNewSpaceList::update(flags_, v);
180   }
181 
weakness_type() const182   WeaknessType weakness_type() const {
183     return NodeWeaknessType::decode(flags_);
184   }
set_weakness_type(WeaknessType weakness_type)185   void set_weakness_type(WeaknessType weakness_type) {
186     flags_ = NodeWeaknessType::update(flags_, weakness_type);
187   }
188 
IsNearDeath() const189   bool IsNearDeath() const {
190     // Check for PENDING to ensure correct answer when processing callbacks.
191     return state() == PENDING || state() == NEAR_DEATH;
192   }
193 
IsWeak() const194   bool IsWeak() const { return state() == WEAK; }
195 
IsInUse() const196   bool IsInUse() const { return state() != FREE; }
197 
IsRetainer() const198   bool IsRetainer() const {
199     return state() != FREE &&
200            !(state() == NEAR_DEATH && weakness_type() != NORMAL_WEAK);
201   }
202 
IsStrongRetainer() const203   bool IsStrongRetainer() const { return state() == NORMAL; }
204 
IsWeakRetainer() const205   bool IsWeakRetainer() const {
206     return state() == WEAK || state() == PENDING ||
207            (state() == NEAR_DEATH && weakness_type() == NORMAL_WEAK);
208   }
209 
MarkPending()210   void MarkPending() {
211     DCHECK(state() == WEAK);
212     set_state(PENDING);
213   }
214 
215   // Independent flag accessors.
MarkIndependent()216   void MarkIndependent() {
217     DCHECK(IsInUse());
218     set_independent(true);
219   }
220 
MarkPartiallyDependent()221   void MarkPartiallyDependent() {
222     DCHECK(IsInUse());
223     if (GetGlobalHandles()->isolate()->heap()->InNewSpace(object_)) {
224       set_partially_dependent(true);
225     }
226   }
clear_partially_dependent()227   void clear_partially_dependent() { set_partially_dependent(false); }
228 
229   // Callback accessor.
230   // TODO(svenpanne) Re-enable or nuke later.
231   // WeakReferenceCallback callback() { return callback_; }
232 
233   // Callback parameter accessors.
set_parameter(void * parameter)234   void set_parameter(void* parameter) {
235     DCHECK(IsInUse());
236     parameter_or_next_free_.parameter = parameter;
237   }
parameter() const238   void* parameter() const {
239     DCHECK(IsInUse());
240     return parameter_or_next_free_.parameter;
241   }
242 
243   // Accessors for next free node in the free list.
next_free()244   Node* next_free() {
245     DCHECK(state() == FREE);
246     return parameter_or_next_free_.next_free;
247   }
set_next_free(Node * value)248   void set_next_free(Node* value) {
249     DCHECK(state() == FREE);
250     parameter_or_next_free_.next_free = value;
251   }
252 
MakeWeak(void * parameter,WeakCallback weak_callback)253   void MakeWeak(void* parameter, WeakCallback weak_callback) {
254     DCHECK(weak_callback != nullptr);
255     DCHECK(IsInUse());
256     CHECK_NE(object_, reinterpret_cast<Object*>(kGlobalHandleZapValue));
257     set_state(WEAK);
258     set_weakness_type(NORMAL_WEAK);
259     set_parameter(parameter);
260     weak_callback_ = weak_callback;
261   }
262 
MakeWeak(void * parameter,WeakCallbackInfo<void>::Callback phantom_callback,v8::WeakCallbackType type)263   void MakeWeak(void* parameter,
264                 WeakCallbackInfo<void>::Callback phantom_callback,
265                 v8::WeakCallbackType type) {
266     DCHECK(phantom_callback != nullptr);
267     DCHECK(IsInUse());
268     CHECK_NE(object_, reinterpret_cast<Object*>(kGlobalHandleZapValue));
269     set_state(WEAK);
270     switch (type) {
271       case v8::WeakCallbackType::kParameter:
272         set_weakness_type(PHANTOM_WEAK);
273         break;
274       case v8::WeakCallbackType::kInternalFields:
275       set_weakness_type(PHANTOM_WEAK_2_INTERNAL_FIELDS);
276       break;
277     }
278     set_parameter(parameter);
279     weak_callback_ = reinterpret_cast<WeakCallback>(phantom_callback);
280   }
281 
ClearWeakness()282   void* ClearWeakness() {
283     DCHECK(IsInUse());
284     void* p = parameter();
285     set_state(NORMAL);
286     set_parameter(NULL);
287     return p;
288   }
289 
CollectPhantomCallbackData(Isolate * isolate,List<PendingPhantomCallback> * pending_phantom_callbacks)290   void CollectPhantomCallbackData(
291       Isolate* isolate,
292       List<PendingPhantomCallback>* pending_phantom_callbacks) {
293     DCHECK(weakness_type() == PHANTOM_WEAK ||
294            weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS);
295     DCHECK(state() == PENDING);
296 
297     void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr,
298                                                                 nullptr};
299     if (weakness_type() != PHANTOM_WEAK && object()->IsJSObject()) {
300       auto jsobject = JSObject::cast(object());
301       int field_count = jsobject->GetInternalFieldCount();
302       for (int i = 0; i < v8::kInternalFieldsInWeakCallback; ++i) {
303         if (field_count == i) break;
304         auto field = jsobject->GetInternalField(i);
305         if (field->IsSmi()) internal_fields[i] = field;
306       }
307     }
308 
309     // Zap with something dangerous.
310     *location() = reinterpret_cast<Object*>(0x6057ca11);
311 
312     typedef v8::WeakCallbackInfo<void> Data;
313     auto callback = reinterpret_cast<Data::Callback>(weak_callback_);
314     pending_phantom_callbacks->Add(
315         PendingPhantomCallback(this, callback, parameter(), internal_fields));
316     DCHECK(IsInUse());
317     set_state(NEAR_DEATH);
318   }
319 
PostGarbageCollectionProcessing(Isolate * isolate)320   bool PostGarbageCollectionProcessing(Isolate* isolate) {
321     // Handles only weak handles (not phantom) that are dying.
322     if (state() != Node::PENDING) return false;
323     if (weak_callback_ == NULL) {
324       Release();
325       return false;
326     }
327     set_state(NEAR_DEATH);
328 
329     // Check that we are not passing a finalized external string to
330     // the callback.
331     DCHECK(!object_->IsExternalOneByteString() ||
332            ExternalOneByteString::cast(object_)->resource() != NULL);
333     DCHECK(!object_->IsExternalTwoByteString() ||
334            ExternalTwoByteString::cast(object_)->resource() != NULL);
335     if (weakness_type() != NORMAL_WEAK) return false;
336 
337     // Leaving V8.
338     VMState<EXTERNAL> vmstate(isolate);
339     HandleScope handle_scope(isolate);
340     Object** object = location();
341     Handle<Object> handle(*object, isolate);
342     v8::WeakCallbackData<v8::Value, void> data(
343         reinterpret_cast<v8::Isolate*>(isolate), parameter(),
344         v8::Utils::ToLocal(handle));
345     set_parameter(NULL);
346     weak_callback_(data);
347 
348     // Absence of explicit cleanup or revival of weak handle
349     // in most of the cases would lead to memory leak.
350     CHECK(state() != NEAR_DEATH);
351     return true;
352   }
353 
354   inline GlobalHandles* GetGlobalHandles();
355 
356  private:
357   inline NodeBlock* FindBlock();
358   inline void IncreaseBlockUses();
359   inline void DecreaseBlockUses();
360 
361   // Storage for object pointer.
362   // Placed first to avoid offset computation.
363   Object* object_;
364 
365   // Next word stores class_id, index, state, and independent.
366   // Note: the most aligned fields should go first.
367 
368   // Wrapper class ID.
369   uint16_t class_id_;
370 
371   // Index in the containing handle block.
372   uint8_t index_;
373 
374   // This stores three flags (independent, partially_dependent and
375   // in_new_space_list) and a State.
376   class NodeState : public BitField<State, 0, 3> {};
377   class IsIndependent : public BitField<bool, 3, 1> {};
378   // The following two fields are mutually exclusive
379   class IsActive : public BitField<bool, 4, 1> {};
380   class IsPartiallyDependent : public BitField<bool, 4, 1> {};
381   class IsInNewSpaceList : public BitField<bool, 5, 1> {};
382   class NodeWeaknessType : public BitField<WeaknessType, 6, 2> {};
383 
384   uint8_t flags_;
385 
386   // Handle specific callback - might be a weak reference in disguise.
387   WeakCallback weak_callback_;
388 
389   // Provided data for callback.  In FREE state, this is used for
390   // the free list link.
391   union {
392     void* parameter;
393     Node* next_free;
394   } parameter_or_next_free_;
395 
396   DISALLOW_COPY_AND_ASSIGN(Node);
397 };
398 
399 
400 class GlobalHandles::NodeBlock {
401  public:
402   static const int kSize = 256;
403 
NodeBlock(GlobalHandles * global_handles,NodeBlock * next)404   explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next)
405       : next_(next),
406         used_nodes_(0),
407         next_used_(NULL),
408         prev_used_(NULL),
409         global_handles_(global_handles) {}
410 
PutNodesOnFreeList(Node ** first_free)411   void PutNodesOnFreeList(Node** first_free) {
412     for (int i = kSize - 1; i >= 0; --i) {
413       nodes_[i].Initialize(i, first_free);
414     }
415   }
416 
node_at(int index)417   Node* node_at(int index) {
418     DCHECK(0 <= index && index < kSize);
419     return &nodes_[index];
420   }
421 
IncreaseUses()422   void IncreaseUses() {
423     DCHECK(used_nodes_ < kSize);
424     if (used_nodes_++ == 0) {
425       NodeBlock* old_first = global_handles_->first_used_block_;
426       global_handles_->first_used_block_ = this;
427       next_used_ = old_first;
428       prev_used_ = NULL;
429       if (old_first == NULL) return;
430       old_first->prev_used_ = this;
431     }
432   }
433 
DecreaseUses()434   void DecreaseUses() {
435     DCHECK(used_nodes_ > 0);
436     if (--used_nodes_ == 0) {
437       if (next_used_ != NULL) next_used_->prev_used_ = prev_used_;
438       if (prev_used_ != NULL) prev_used_->next_used_ = next_used_;
439       if (this == global_handles_->first_used_block_) {
440         global_handles_->first_used_block_ = next_used_;
441       }
442     }
443   }
444 
global_handles()445   GlobalHandles* global_handles() { return global_handles_; }
446 
447   // Next block in the list of all blocks.
next() const448   NodeBlock* next() const { return next_; }
449 
450   // Next/previous block in the list of blocks with used nodes.
next_used() const451   NodeBlock* next_used() const { return next_used_; }
prev_used() const452   NodeBlock* prev_used() const { return prev_used_; }
453 
454  private:
455   Node nodes_[kSize];
456   NodeBlock* const next_;
457   int used_nodes_;
458   NodeBlock* next_used_;
459   NodeBlock* prev_used_;
460   GlobalHandles* global_handles_;
461 };
462 
463 
GetGlobalHandles()464 GlobalHandles* GlobalHandles::Node::GetGlobalHandles() {
465   return FindBlock()->global_handles();
466 }
467 
468 
FindBlock()469 GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() {
470   intptr_t ptr = reinterpret_cast<intptr_t>(this);
471   ptr = ptr - index_ * sizeof(Node);
472   NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr);
473   DCHECK(block->node_at(index_) == this);
474   return block;
475 }
476 
477 
IncreaseBlockUses()478 void GlobalHandles::Node::IncreaseBlockUses() {
479   NodeBlock* node_block = FindBlock();
480   node_block->IncreaseUses();
481   GlobalHandles* global_handles = node_block->global_handles();
482   global_handles->isolate()->counters()->global_handles()->Increment();
483   global_handles->number_of_global_handles_++;
484 }
485 
486 
DecreaseBlockUses()487 void GlobalHandles::Node::DecreaseBlockUses() {
488   NodeBlock* node_block = FindBlock();
489   GlobalHandles* global_handles = node_block->global_handles();
490   parameter_or_next_free_.next_free = global_handles->first_free_;
491   global_handles->first_free_ = this;
492   node_block->DecreaseUses();
493   global_handles->isolate()->counters()->global_handles()->Decrement();
494   global_handles->number_of_global_handles_--;
495 }
496 
497 
498 class GlobalHandles::NodeIterator {
499  public:
NodeIterator(GlobalHandles * global_handles)500   explicit NodeIterator(GlobalHandles* global_handles)
501       : block_(global_handles->first_used_block_),
502         index_(0) {}
503 
done() const504   bool done() const { return block_ == NULL; }
505 
node() const506   Node* node() const {
507     DCHECK(!done());
508     return block_->node_at(index_);
509   }
510 
Advance()511   void Advance() {
512     DCHECK(!done());
513     if (++index_ < NodeBlock::kSize) return;
514     index_ = 0;
515     block_ = block_->next_used();
516   }
517 
518  private:
519   NodeBlock* block_;
520   int index_;
521 
522   DISALLOW_COPY_AND_ASSIGN(NodeIterator);
523 };
524 
525 class GlobalHandles::PendingPhantomCallbacksSecondPassTask
526     : public v8::internal::CancelableTask {
527  public:
528   // Takes ownership of the contents of pending_phantom_callbacks, leaving it in
529   // the same state it would be after a call to Clear().
PendingPhantomCallbacksSecondPassTask(List<PendingPhantomCallback> * pending_phantom_callbacks,Isolate * isolate)530   PendingPhantomCallbacksSecondPassTask(
531       List<PendingPhantomCallback>* pending_phantom_callbacks, Isolate* isolate)
532       : CancelableTask(isolate) {
533     pending_phantom_callbacks_.Swap(pending_phantom_callbacks);
534   }
535 
RunInternal()536   void RunInternal() override {
537     isolate()->heap()->CallGCPrologueCallbacks(
538         GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags);
539     InvokeSecondPassPhantomCallbacks(&pending_phantom_callbacks_, isolate());
540     isolate()->heap()->CallGCEpilogueCallbacks(
541         GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags);
542   }
543 
544  private:
545   List<PendingPhantomCallback> pending_phantom_callbacks_;
546 
547   DISALLOW_COPY_AND_ASSIGN(PendingPhantomCallbacksSecondPassTask);
548 };
549 
550 
GlobalHandles(Isolate * isolate)551 GlobalHandles::GlobalHandles(Isolate* isolate)
552     : isolate_(isolate),
553       number_of_global_handles_(0),
554       first_block_(NULL),
555       first_used_block_(NULL),
556       first_free_(NULL),
557       post_gc_processing_count_(0),
558       object_group_connections_(kObjectGroupConnectionsCapacity) {}
559 
560 
~GlobalHandles()561 GlobalHandles::~GlobalHandles() {
562   NodeBlock* block = first_block_;
563   while (block != NULL) {
564     NodeBlock* tmp = block->next();
565     delete block;
566     block = tmp;
567   }
568   first_block_ = NULL;
569 }
570 
571 
Create(Object * value)572 Handle<Object> GlobalHandles::Create(Object* value) {
573   if (first_free_ == NULL) {
574     first_block_ = new NodeBlock(this, first_block_);
575     first_block_->PutNodesOnFreeList(&first_free_);
576   }
577   DCHECK(first_free_ != NULL);
578   // Take the first node in the free list.
579   Node* result = first_free_;
580   first_free_ = result->next_free();
581   result->Acquire(value);
582   if (isolate_->heap()->InNewSpace(value) &&
583       !result->is_in_new_space_list()) {
584     new_space_nodes_.Add(result);
585     result->set_in_new_space_list(true);
586   }
587   return result->handle();
588 }
589 
590 
CopyGlobal(Object ** location)591 Handle<Object> GlobalHandles::CopyGlobal(Object** location) {
592   DCHECK(location != NULL);
593   return Node::FromLocation(location)->GetGlobalHandles()->Create(*location);
594 }
595 
596 
Destroy(Object ** location)597 void GlobalHandles::Destroy(Object** location) {
598   if (location != NULL) Node::FromLocation(location)->Release();
599 }
600 
601 
MakeWeak(Object ** location,void * parameter,WeakCallback weak_callback)602 void GlobalHandles::MakeWeak(Object** location, void* parameter,
603                              WeakCallback weak_callback) {
604   Node::FromLocation(location)->MakeWeak(parameter, weak_callback);
605 }
606 
607 
608 typedef v8::WeakCallbackInfo<void>::Callback GenericCallback;
609 
610 
MakeWeak(Object ** location,void * parameter,GenericCallback phantom_callback,v8::WeakCallbackType type)611 void GlobalHandles::MakeWeak(Object** location, void* parameter,
612                              GenericCallback phantom_callback,
613                              v8::WeakCallbackType type) {
614   Node::FromLocation(location)->MakeWeak(parameter, phantom_callback, type);
615 }
616 
617 
ClearWeakness(Object ** location)618 void* GlobalHandles::ClearWeakness(Object** location) {
619   return Node::FromLocation(location)->ClearWeakness();
620 }
621 
622 
MarkIndependent(Object ** location)623 void GlobalHandles::MarkIndependent(Object** location) {
624   Node::FromLocation(location)->MarkIndependent();
625 }
626 
627 
MarkPartiallyDependent(Object ** location)628 void GlobalHandles::MarkPartiallyDependent(Object** location) {
629   Node::FromLocation(location)->MarkPartiallyDependent();
630 }
631 
632 
IsIndependent(Object ** location)633 bool GlobalHandles::IsIndependent(Object** location) {
634   return Node::FromLocation(location)->is_independent();
635 }
636 
637 
IsNearDeath(Object ** location)638 bool GlobalHandles::IsNearDeath(Object** location) {
639   return Node::FromLocation(location)->IsNearDeath();
640 }
641 
642 
IsWeak(Object ** location)643 bool GlobalHandles::IsWeak(Object** location) {
644   return Node::FromLocation(location)->IsWeak();
645 }
646 
647 
IterateWeakRoots(ObjectVisitor * v)648 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
649   for (NodeIterator it(this); !it.done(); it.Advance()) {
650     Node* node = it.node();
651     if (node->IsWeakRetainer()) {
652       // Pending weak phantom handles die immediately. Everything else survives.
653       if (node->state() == Node::PENDING &&
654           node->weakness_type() != NORMAL_WEAK) {
655           node->CollectPhantomCallbackData(isolate(),
656                                            &pending_phantom_callbacks_);
657       } else {
658         v->VisitPointer(node->location());
659       }
660     }
661   }
662 }
663 
664 
IdentifyWeakHandles(WeakSlotCallback f)665 void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
666   for (NodeIterator it(this); !it.done(); it.Advance()) {
667     if (it.node()->IsWeak() && f(it.node()->location())) {
668       it.node()->MarkPending();
669     }
670   }
671 }
672 
673 
IterateNewSpaceStrongAndDependentRoots(ObjectVisitor * v)674 void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
675   for (int i = 0; i < new_space_nodes_.length(); ++i) {
676     Node* node = new_space_nodes_[i];
677     if (FLAG_scavenge_reclaim_unmodified_objects) {
678       if (node->IsStrongRetainer() ||
679           (node->IsWeakRetainer() && !node->is_independent() &&
680            node->is_active())) {
681         v->VisitPointer(node->location());
682       }
683     } else {
684       if (node->IsStrongRetainer() ||
685           (node->IsWeakRetainer() && !node->is_independent() &&
686            !node->is_partially_dependent())) {
687         v->VisitPointer(node->location());
688       }
689     }
690   }
691 }
692 
693 
IdentifyNewSpaceWeakIndependentHandles(WeakSlotCallbackWithHeap f)694 void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles(
695     WeakSlotCallbackWithHeap f) {
696   for (int i = 0; i < new_space_nodes_.length(); ++i) {
697     Node* node = new_space_nodes_[i];
698     DCHECK(node->is_in_new_space_list());
699     if ((node->is_independent() || node->is_partially_dependent()) &&
700         node->IsWeak() && f(isolate_->heap(), node->location())) {
701       node->MarkPending();
702     }
703   }
704 }
705 
706 
IterateNewSpaceWeakIndependentRoots(ObjectVisitor * v)707 void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
708   for (int i = 0; i < new_space_nodes_.length(); ++i) {
709     Node* node = new_space_nodes_[i];
710     DCHECK(node->is_in_new_space_list());
711     if ((node->is_independent() || node->is_partially_dependent()) &&
712         node->IsWeakRetainer()) {
713       // Pending weak phantom handles die immediately. Everything else survives.
714       if (node->state() == Node::PENDING &&
715           node->weakness_type() != NORMAL_WEAK) {
716         node->CollectPhantomCallbackData(isolate(),
717                                          &pending_phantom_callbacks_);
718       } else {
719         v->VisitPointer(node->location());
720       }
721     }
722   }
723 }
724 
725 
IdentifyWeakUnmodifiedObjects(WeakSlotCallback is_unmodified)726 void GlobalHandles::IdentifyWeakUnmodifiedObjects(
727     WeakSlotCallback is_unmodified) {
728   for (int i = 0; i < new_space_nodes_.length(); ++i) {
729     Node* node = new_space_nodes_[i];
730     if (node->IsWeak() && !is_unmodified(node->location())) {
731       node->set_active(true);
732     }
733   }
734 }
735 
736 
MarkNewSpaceWeakUnmodifiedObjectsPending(WeakSlotCallbackWithHeap is_unscavenged)737 void GlobalHandles::MarkNewSpaceWeakUnmodifiedObjectsPending(
738     WeakSlotCallbackWithHeap is_unscavenged) {
739   for (int i = 0; i < new_space_nodes_.length(); ++i) {
740     Node* node = new_space_nodes_[i];
741     DCHECK(node->is_in_new_space_list());
742     if ((node->is_independent() || !node->is_active()) && node->IsWeak() &&
743         is_unscavenged(isolate_->heap(), node->location())) {
744       node->MarkPending();
745     }
746   }
747 }
748 
749 
IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor * v)750 void GlobalHandles::IterateNewSpaceWeakUnmodifiedRoots(ObjectVisitor* v) {
751   for (int i = 0; i < new_space_nodes_.length(); ++i) {
752     Node* node = new_space_nodes_[i];
753     DCHECK(node->is_in_new_space_list());
754     if ((node->is_independent() || !node->is_active()) &&
755         node->IsWeakRetainer()) {
756       // Pending weak phantom handles die immediately. Everything else survives.
757       if (node->state() == Node::PENDING &&
758           node->weakness_type() != NORMAL_WEAK) {
759         node->CollectPhantomCallbackData(isolate(),
760                                          &pending_phantom_callbacks_);
761       } else {
762         v->VisitPointer(node->location());
763       }
764     }
765   }
766 }
767 
768 
IterateObjectGroups(ObjectVisitor * v,WeakSlotCallbackWithHeap can_skip)769 bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
770                                         WeakSlotCallbackWithHeap can_skip) {
771   ComputeObjectGroupsAndImplicitReferences();
772   int last = 0;
773   bool any_group_was_visited = false;
774   for (int i = 0; i < object_groups_.length(); i++) {
775     ObjectGroup* entry = object_groups_.at(i);
776     DCHECK(entry != NULL);
777 
778     Object*** objects = entry->objects;
779     bool group_should_be_visited = false;
780     for (size_t j = 0; j < entry->length; j++) {
781       Object* object = *objects[j];
782       if (object->IsHeapObject()) {
783         if (!can_skip(isolate_->heap(), &object)) {
784           group_should_be_visited = true;
785           break;
786         }
787       }
788     }
789 
790     if (!group_should_be_visited) {
791       object_groups_[last++] = entry;
792       continue;
793     }
794 
795     // An object in the group requires visiting, so iterate over all
796     // objects in the group.
797     for (size_t j = 0; j < entry->length; ++j) {
798       Object* object = *objects[j];
799       if (object->IsHeapObject()) {
800         v->VisitPointer(&object);
801         any_group_was_visited = true;
802       }
803     }
804 
805     // Once the entire group has been iterated over, set the object
806     // group to NULL so it won't be processed again.
807     delete entry;
808     object_groups_.at(i) = NULL;
809   }
810   object_groups_.Rewind(last);
811   return any_group_was_visited;
812 }
813 
814 
InvokeSecondPassPhantomCallbacks(List<PendingPhantomCallback> * callbacks,Isolate * isolate)815 void GlobalHandles::InvokeSecondPassPhantomCallbacks(
816     List<PendingPhantomCallback>* callbacks, Isolate* isolate) {
817   while (callbacks->length() != 0) {
818     auto callback = callbacks->RemoveLast();
819     DCHECK(callback.node() == nullptr);
820     // No second pass callback required.
821     if (callback.callback() == nullptr) continue;
822     // Fire second pass callback
823     callback.Invoke(isolate);
824   }
825 }
826 
827 
PostScavengeProcessing(const int initial_post_gc_processing_count)828 int GlobalHandles::PostScavengeProcessing(
829     const int initial_post_gc_processing_count) {
830   int freed_nodes = 0;
831   for (int i = 0; i < new_space_nodes_.length(); ++i) {
832     Node* node = new_space_nodes_[i];
833     DCHECK(node->is_in_new_space_list());
834     if (!node->IsRetainer()) {
835       // Free nodes do not have weak callbacks. Do not use them to compute
836       // the freed_nodes.
837       continue;
838     }
839     // Skip dependent or unmodified handles. Their weak callbacks might expect
840     // to be
841     // called between two global garbage collection callbacks which
842     // are not called for minor collections.
843     if (FLAG_scavenge_reclaim_unmodified_objects) {
844       if (!node->is_independent() && (node->is_active())) {
845         node->set_active(false);
846         continue;
847       }
848       node->set_active(false);
849     } else {
850       if (!node->is_independent() && !node->is_partially_dependent()) {
851         continue;
852       }
853       node->clear_partially_dependent();
854     }
855 
856     if (node->PostGarbageCollectionProcessing(isolate_)) {
857       if (initial_post_gc_processing_count != post_gc_processing_count_) {
858         // Weak callback triggered another GC and another round of
859         // PostGarbageCollection processing.  The current node might
860         // have been deleted in that round, so we need to bail out (or
861         // restart the processing).
862         return freed_nodes;
863       }
864     }
865     if (!node->IsRetainer()) {
866       freed_nodes++;
867     }
868   }
869   return freed_nodes;
870 }
871 
872 
PostMarkSweepProcessing(const int initial_post_gc_processing_count)873 int GlobalHandles::PostMarkSweepProcessing(
874     const int initial_post_gc_processing_count) {
875   int freed_nodes = 0;
876   for (NodeIterator it(this); !it.done(); it.Advance()) {
877     if (!it.node()->IsRetainer()) {
878       // Free nodes do not have weak callbacks. Do not use them to compute
879       // the freed_nodes.
880       continue;
881     }
882     if (FLAG_scavenge_reclaim_unmodified_objects) {
883       it.node()->set_active(false);
884     } else {
885       it.node()->clear_partially_dependent();
886     }
887     if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
888       if (initial_post_gc_processing_count != post_gc_processing_count_) {
889         // See the comment above.
890         return freed_nodes;
891       }
892     }
893     if (!it.node()->IsRetainer()) {
894       freed_nodes++;
895     }
896   }
897   return freed_nodes;
898 }
899 
900 
UpdateListOfNewSpaceNodes()901 void GlobalHandles::UpdateListOfNewSpaceNodes() {
902   int last = 0;
903   for (int i = 0; i < new_space_nodes_.length(); ++i) {
904     Node* node = new_space_nodes_[i];
905     DCHECK(node->is_in_new_space_list());
906     if (node->IsRetainer()) {
907       if (isolate_->heap()->InNewSpace(node->object())) {
908         new_space_nodes_[last++] = node;
909         isolate_->heap()->IncrementNodesCopiedInNewSpace();
910       } else {
911         node->set_in_new_space_list(false);
912         isolate_->heap()->IncrementNodesPromoted();
913       }
914     } else {
915       node->set_in_new_space_list(false);
916       isolate_->heap()->IncrementNodesDiedInNewSpace();
917     }
918   }
919   new_space_nodes_.Rewind(last);
920   new_space_nodes_.Trim();
921 }
922 
923 
DispatchPendingPhantomCallbacks(bool synchronous_second_pass)924 int GlobalHandles::DispatchPendingPhantomCallbacks(
925     bool synchronous_second_pass) {
926   int freed_nodes = 0;
927   {
928     // The initial pass callbacks must simply clear the nodes.
929     for (auto i = pending_phantom_callbacks_.begin();
930          i != pending_phantom_callbacks_.end(); ++i) {
931       auto callback = i;
932       // Skip callbacks that have already been processed once.
933       if (callback->node() == nullptr) continue;
934       callback->Invoke(isolate());
935       freed_nodes++;
936     }
937   }
938   if (pending_phantom_callbacks_.length() > 0) {
939     if (FLAG_optimize_for_size || FLAG_predictable || synchronous_second_pass) {
940       isolate()->heap()->CallGCPrologueCallbacks(
941           GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags);
942       InvokeSecondPassPhantomCallbacks(&pending_phantom_callbacks_, isolate());
943       isolate()->heap()->CallGCEpilogueCallbacks(
944           GCType::kGCTypeProcessWeakCallbacks, kNoGCCallbackFlags);
945     } else {
946       auto task = new PendingPhantomCallbacksSecondPassTask(
947           &pending_phantom_callbacks_, isolate());
948       V8::GetCurrentPlatform()->CallOnForegroundThread(
949           reinterpret_cast<v8::Isolate*>(isolate()), task);
950     }
951   }
952   pending_phantom_callbacks_.Clear();
953   return freed_nodes;
954 }
955 
956 
Invoke(Isolate * isolate)957 void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) {
958   Data::Callback* callback_addr = nullptr;
959   if (node_ != nullptr) {
960     // Initialize for first pass callback.
961     DCHECK(node_->state() == Node::NEAR_DEATH);
962     callback_addr = &callback_;
963   }
964   Data data(reinterpret_cast<v8::Isolate*>(isolate), parameter_,
965             internal_fields_, callback_addr);
966   Data::Callback callback = callback_;
967   callback_ = nullptr;
968   callback(data);
969   if (node_ != nullptr) {
970     // Transition to second pass state.
971     DCHECK(node_->state() == Node::FREE);
972     node_ = nullptr;
973   }
974 }
975 
976 
PostGarbageCollectionProcessing(GarbageCollector collector,const v8::GCCallbackFlags gc_callback_flags)977 int GlobalHandles::PostGarbageCollectionProcessing(
978     GarbageCollector collector, const v8::GCCallbackFlags gc_callback_flags) {
979   // Process weak global handle callbacks. This must be done after the
980   // GC is completely done, because the callbacks may invoke arbitrary
981   // API functions.
982   DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
983   const int initial_post_gc_processing_count = ++post_gc_processing_count_;
984   int freed_nodes = 0;
985   bool synchronous_second_pass =
986       (gc_callback_flags &
987        (kGCCallbackFlagForced |
988         kGCCallbackFlagSynchronousPhantomCallbackProcessing)) != 0;
989   freed_nodes += DispatchPendingPhantomCallbacks(synchronous_second_pass);
990   if (initial_post_gc_processing_count != post_gc_processing_count_) {
991     // If the callbacks caused a nested GC, then return.  See comment in
992     // PostScavengeProcessing.
993     return freed_nodes;
994   }
995   if (collector == SCAVENGER) {
996     freed_nodes += PostScavengeProcessing(initial_post_gc_processing_count);
997   } else {
998     freed_nodes += PostMarkSweepProcessing(initial_post_gc_processing_count);
999   }
1000   if (initial_post_gc_processing_count != post_gc_processing_count_) {
1001     // If the callbacks caused a nested GC, then return.  See comment in
1002     // PostScavengeProcessing.
1003     return freed_nodes;
1004   }
1005   if (initial_post_gc_processing_count == post_gc_processing_count_) {
1006     UpdateListOfNewSpaceNodes();
1007   }
1008   return freed_nodes;
1009 }
1010 
1011 
IterateStrongRoots(ObjectVisitor * v)1012 void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
1013   for (NodeIterator it(this); !it.done(); it.Advance()) {
1014     if (it.node()->IsStrongRetainer()) {
1015       v->VisitPointer(it.node()->location());
1016     }
1017   }
1018 }
1019 
1020 
IterateAllRoots(ObjectVisitor * v)1021 void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
1022   for (NodeIterator it(this); !it.done(); it.Advance()) {
1023     if (it.node()->IsRetainer()) {
1024       v->VisitPointer(it.node()->location());
1025     }
1026   }
1027 }
1028 
1029 
IterateAllRootsWithClassIds(ObjectVisitor * v)1030 void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
1031   for (NodeIterator it(this); !it.done(); it.Advance()) {
1032     if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) {
1033       v->VisitEmbedderReference(it.node()->location(),
1034                                 it.node()->wrapper_class_id());
1035     }
1036   }
1037 }
1038 
1039 
IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor * v)1040 void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
1041   for (int i = 0; i < new_space_nodes_.length(); ++i) {
1042     Node* node = new_space_nodes_[i];
1043     if (node->IsRetainer() && node->has_wrapper_class_id()) {
1044       v->VisitEmbedderReference(node->location(),
1045                                 node->wrapper_class_id());
1046     }
1047   }
1048 }
1049 
1050 
IterateWeakRootsInNewSpaceWithClassIds(ObjectVisitor * v)1051 void GlobalHandles::IterateWeakRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
1052   for (int i = 0; i < new_space_nodes_.length(); ++i) {
1053     Node* node = new_space_nodes_[i];
1054     if (node->has_wrapper_class_id() && node->IsWeak()) {
1055       v->VisitEmbedderReference(node->location(), node->wrapper_class_id());
1056     }
1057   }
1058 }
1059 
1060 
NumberOfWeakHandles()1061 int GlobalHandles::NumberOfWeakHandles() {
1062   int count = 0;
1063   for (NodeIterator it(this); !it.done(); it.Advance()) {
1064     if (it.node()->IsWeakRetainer()) {
1065       count++;
1066     }
1067   }
1068   return count;
1069 }
1070 
1071 
NumberOfGlobalObjectWeakHandles()1072 int GlobalHandles::NumberOfGlobalObjectWeakHandles() {
1073   int count = 0;
1074   for (NodeIterator it(this); !it.done(); it.Advance()) {
1075     if (it.node()->IsWeakRetainer() &&
1076         it.node()->object()->IsJSGlobalObject()) {
1077       count++;
1078     }
1079   }
1080   return count;
1081 }
1082 
1083 
RecordStats(HeapStats * stats)1084 void GlobalHandles::RecordStats(HeapStats* stats) {
1085   *stats->global_handle_count = 0;
1086   *stats->weak_global_handle_count = 0;
1087   *stats->pending_global_handle_count = 0;
1088   *stats->near_death_global_handle_count = 0;
1089   *stats->free_global_handle_count = 0;
1090   for (NodeIterator it(this); !it.done(); it.Advance()) {
1091     *stats->global_handle_count += 1;
1092     if (it.node()->state() == Node::WEAK) {
1093       *stats->weak_global_handle_count += 1;
1094     } else if (it.node()->state() == Node::PENDING) {
1095       *stats->pending_global_handle_count += 1;
1096     } else if (it.node()->state() == Node::NEAR_DEATH) {
1097       *stats->near_death_global_handle_count += 1;
1098     } else if (it.node()->state() == Node::FREE) {
1099       *stats->free_global_handle_count += 1;
1100     }
1101   }
1102 }
1103 
1104 #ifdef DEBUG
1105 
PrintStats()1106 void GlobalHandles::PrintStats() {
1107   int total = 0;
1108   int weak = 0;
1109   int pending = 0;
1110   int near_death = 0;
1111   int destroyed = 0;
1112 
1113   for (NodeIterator it(this); !it.done(); it.Advance()) {
1114     total++;
1115     if (it.node()->state() == Node::WEAK) weak++;
1116     if (it.node()->state() == Node::PENDING) pending++;
1117     if (it.node()->state() == Node::NEAR_DEATH) near_death++;
1118     if (it.node()->state() == Node::FREE) destroyed++;
1119   }
1120 
1121   PrintF("Global Handle Statistics:\n");
1122   PrintF("  allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
1123   PrintF("  # weak       = %d\n", weak);
1124   PrintF("  # pending    = %d\n", pending);
1125   PrintF("  # near_death = %d\n", near_death);
1126   PrintF("  # free       = %d\n", destroyed);
1127   PrintF("  # total      = %d\n", total);
1128 }
1129 
1130 
Print()1131 void GlobalHandles::Print() {
1132   PrintF("Global handles:\n");
1133   for (NodeIterator it(this); !it.done(); it.Advance()) {
1134     PrintF("  handle %p to %p%s\n",
1135            reinterpret_cast<void*>(it.node()->location()),
1136            reinterpret_cast<void*>(it.node()->object()),
1137            it.node()->IsWeak() ? " (weak)" : "");
1138   }
1139 }
1140 
1141 #endif
1142 
1143 
1144 
AddObjectGroup(Object *** handles,size_t length,v8::RetainedObjectInfo * info)1145 void GlobalHandles::AddObjectGroup(Object*** handles,
1146                                    size_t length,
1147                                    v8::RetainedObjectInfo* info) {
1148 #ifdef DEBUG
1149   for (size_t i = 0; i < length; ++i) {
1150     DCHECK(!Node::FromLocation(handles[i])->is_independent());
1151   }
1152 #endif
1153   if (length == 0) {
1154     if (info != NULL) info->Dispose();
1155     return;
1156   }
1157   ObjectGroup* group = new ObjectGroup(length);
1158   for (size_t i = 0; i < length; ++i)
1159     group->objects[i] = handles[i];
1160   group->info = info;
1161   object_groups_.Add(group);
1162 }
1163 
1164 
SetObjectGroupId(Object ** handle,UniqueId id)1165 void GlobalHandles::SetObjectGroupId(Object** handle,
1166                                      UniqueId id) {
1167   object_group_connections_.Add(ObjectGroupConnection(id, handle));
1168 }
1169 
1170 
SetRetainedObjectInfo(UniqueId id,RetainedObjectInfo * info)1171 void GlobalHandles::SetRetainedObjectInfo(UniqueId id,
1172                                           RetainedObjectInfo* info) {
1173   retainer_infos_.Add(ObjectGroupRetainerInfo(id, info));
1174 }
1175 
1176 
SetReferenceFromGroup(UniqueId id,Object ** child)1177 void GlobalHandles::SetReferenceFromGroup(UniqueId id, Object** child) {
1178   DCHECK(!Node::FromLocation(child)->is_independent());
1179   implicit_ref_connections_.Add(ObjectGroupConnection(id, child));
1180 }
1181 
1182 
SetReference(HeapObject ** parent,Object ** child)1183 void GlobalHandles::SetReference(HeapObject** parent, Object** child) {
1184   DCHECK(!Node::FromLocation(child)->is_independent());
1185   ImplicitRefGroup* group = new ImplicitRefGroup(parent, 1);
1186   group->children[0] = child;
1187   implicit_ref_groups_.Add(group);
1188 }
1189 
1190 
RemoveObjectGroups()1191 void GlobalHandles::RemoveObjectGroups() {
1192   for (int i = 0; i < object_groups_.length(); i++)
1193     delete object_groups_.at(i);
1194   object_groups_.Clear();
1195   for (int i = 0; i < retainer_infos_.length(); ++i)
1196     retainer_infos_[i].info->Dispose();
1197   retainer_infos_.Clear();
1198   object_group_connections_.Clear();
1199   object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
1200 }
1201 
1202 
RemoveImplicitRefGroups()1203 void GlobalHandles::RemoveImplicitRefGroups() {
1204   for (int i = 0; i < implicit_ref_groups_.length(); i++) {
1205     delete implicit_ref_groups_.at(i);
1206   }
1207   implicit_ref_groups_.Clear();
1208   implicit_ref_connections_.Clear();
1209 }
1210 
1211 
TearDown()1212 void GlobalHandles::TearDown() {
1213   // TODO(1428): invoke weak callbacks.
1214 }
1215 
1216 
ComputeObjectGroupsAndImplicitReferences()1217 void GlobalHandles::ComputeObjectGroupsAndImplicitReferences() {
1218   if (object_group_connections_.length() == 0) {
1219     for (int i = 0; i < retainer_infos_.length(); ++i)
1220       retainer_infos_[i].info->Dispose();
1221     retainer_infos_.Clear();
1222     implicit_ref_connections_.Clear();
1223     return;
1224   }
1225 
1226   object_group_connections_.Sort();
1227   retainer_infos_.Sort();
1228   implicit_ref_connections_.Sort();
1229 
1230   int info_index = 0;  // For iterating retainer_infos_.
1231   UniqueId current_group_id(0);
1232   int current_group_start = 0;
1233 
1234   int current_implicit_refs_start = 0;
1235   int current_implicit_refs_end = 0;
1236   for (int i = 0; i <= object_group_connections_.length(); ++i) {
1237     if (i == 0)
1238       current_group_id = object_group_connections_[i].id;
1239     if (i == object_group_connections_.length() ||
1240         current_group_id != object_group_connections_[i].id) {
1241       // Group detected: objects in indices [current_group_start, i[.
1242 
1243       // Find out which implicit references are related to this group. (We want
1244       // to ignore object groups which only have 1 object, but that object is
1245       // needed as a representative object for the implicit refrerence group.)
1246       while (current_implicit_refs_start < implicit_ref_connections_.length() &&
1247              implicit_ref_connections_[current_implicit_refs_start].id <
1248                  current_group_id)
1249         ++current_implicit_refs_start;
1250       current_implicit_refs_end = current_implicit_refs_start;
1251       while (current_implicit_refs_end < implicit_ref_connections_.length() &&
1252              implicit_ref_connections_[current_implicit_refs_end].id ==
1253                  current_group_id)
1254         ++current_implicit_refs_end;
1255 
1256       if (current_implicit_refs_end > current_implicit_refs_start) {
1257         // Find a representative object for the implicit references.
1258         HeapObject** representative = NULL;
1259         for (int j = current_group_start; j < i; ++j) {
1260           Object** object = object_group_connections_[j].object;
1261           if ((*object)->IsHeapObject()) {
1262             representative = reinterpret_cast<HeapObject**>(object);
1263             break;
1264           }
1265         }
1266         if (representative) {
1267           ImplicitRefGroup* group = new ImplicitRefGroup(
1268               representative,
1269               current_implicit_refs_end - current_implicit_refs_start);
1270           for (int j = current_implicit_refs_start;
1271                j < current_implicit_refs_end;
1272                ++j) {
1273             group->children[j - current_implicit_refs_start] =
1274                 implicit_ref_connections_[j].object;
1275           }
1276           implicit_ref_groups_.Add(group);
1277         }
1278         current_implicit_refs_start = current_implicit_refs_end;
1279       }
1280 
1281       // Find a RetainedObjectInfo for the group.
1282       RetainedObjectInfo* info = NULL;
1283       while (info_index < retainer_infos_.length() &&
1284              retainer_infos_[info_index].id < current_group_id) {
1285         retainer_infos_[info_index].info->Dispose();
1286         ++info_index;
1287       }
1288       if (info_index < retainer_infos_.length() &&
1289           retainer_infos_[info_index].id == current_group_id) {
1290         // This object group has an associated ObjectGroupRetainerInfo.
1291         info = retainer_infos_[info_index].info;
1292         ++info_index;
1293       }
1294 
1295       // Ignore groups which only contain one object.
1296       if (i > current_group_start + 1) {
1297         ObjectGroup* group = new ObjectGroup(i - current_group_start);
1298         for (int j = current_group_start; j < i; ++j) {
1299           group->objects[j - current_group_start] =
1300               object_group_connections_[j].object;
1301         }
1302         group->info = info;
1303         object_groups_.Add(group);
1304       } else if (info) {
1305         info->Dispose();
1306       }
1307 
1308       if (i < object_group_connections_.length()) {
1309         current_group_id = object_group_connections_[i].id;
1310         current_group_start = i;
1311       }
1312     }
1313   }
1314   object_group_connections_.Clear();
1315   object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
1316   retainer_infos_.Clear();
1317   implicit_ref_connections_.Clear();
1318 }
1319 
1320 
EternalHandles()1321 EternalHandles::EternalHandles() : size_(0) {
1322   for (unsigned i = 0; i < arraysize(singleton_handles_); i++) {
1323     singleton_handles_[i] = kInvalidIndex;
1324   }
1325 }
1326 
1327 
~EternalHandles()1328 EternalHandles::~EternalHandles() {
1329   for (int i = 0; i < blocks_.length(); i++) delete[] blocks_[i];
1330 }
1331 
1332 
IterateAllRoots(ObjectVisitor * visitor)1333 void EternalHandles::IterateAllRoots(ObjectVisitor* visitor) {
1334   int limit = size_;
1335   for (int i = 0; i < blocks_.length(); i++) {
1336     DCHECK(limit > 0);
1337     Object** block = blocks_[i];
1338     visitor->VisitPointers(block, block + Min(limit, kSize));
1339     limit -= kSize;
1340   }
1341 }
1342 
1343 
IterateNewSpaceRoots(ObjectVisitor * visitor)1344 void EternalHandles::IterateNewSpaceRoots(ObjectVisitor* visitor) {
1345   for (int i = 0; i < new_space_indices_.length(); i++) {
1346     visitor->VisitPointer(GetLocation(new_space_indices_[i]));
1347   }
1348 }
1349 
1350 
PostGarbageCollectionProcessing(Heap * heap)1351 void EternalHandles::PostGarbageCollectionProcessing(Heap* heap) {
1352   int last = 0;
1353   for (int i = 0; i < new_space_indices_.length(); i++) {
1354     int index = new_space_indices_[i];
1355     if (heap->InNewSpace(*GetLocation(index))) {
1356       new_space_indices_[last++] = index;
1357     }
1358   }
1359   new_space_indices_.Rewind(last);
1360 }
1361 
1362 
Create(Isolate * isolate,Object * object,int * index)1363 void EternalHandles::Create(Isolate* isolate, Object* object, int* index) {
1364   DCHECK_EQ(kInvalidIndex, *index);
1365   if (object == NULL) return;
1366   DCHECK_NE(isolate->heap()->the_hole_value(), object);
1367   int block = size_ >> kShift;
1368   int offset = size_ & kMask;
1369   // need to resize
1370   if (offset == 0) {
1371     Object** next_block = new Object*[kSize];
1372     Object* the_hole = isolate->heap()->the_hole_value();
1373     MemsetPointer(next_block, the_hole, kSize);
1374     blocks_.Add(next_block);
1375   }
1376   DCHECK_EQ(isolate->heap()->the_hole_value(), blocks_[block][offset]);
1377   blocks_[block][offset] = object;
1378   if (isolate->heap()->InNewSpace(object)) {
1379     new_space_indices_.Add(size_);
1380   }
1381   *index = size_++;
1382 }
1383 
1384 
1385 }  // namespace internal
1386 }  // namespace v8
1387