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