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