global-handles.cc 36.3 KB
Newer Older
1
// Copyright 2009 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/v8.h"
6

7 8
#include "src/api.h"
#include "src/global-handles.h"
9

10
#include "src/vm-state-inl.h"
11

12 13
namespace v8 {
namespace internal {
14

15

16
ObjectGroup::~ObjectGroup() {
17 18 19 20 21 22 23
  if (info != NULL) info->Dispose();
  delete[] objects;
}


ImplicitRefGroup::~ImplicitRefGroup() {
  delete[] children;
24 25 26
}


27
class GlobalHandles::Node {
28
 public:
29 30 31
  // State transition diagram:
  // FREE -> NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, FREE }
  enum State {
32
    FREE = 0,
33 34 35 36 37
    NORMAL,      // Normal global handle.
    WEAK,        // Flagged as weak but not yet finalized.
    PENDING,     // Has been recognized as only reachable by weak handles.
    NEAR_DEATH,  // Callback has informed the handle is near death.
    NUMBER_OF_NODE_STATES
38
  };
39

40 41
  // Maps handle location (slot) to the containing node.
  static Node* FromLocation(Object** location) {
42
    DCHECK(OFFSET_OF(Node, object_) == 0);
43
    return reinterpret_cast<Node*>(location);
44 45
  }

46
  Node() {
47 48
    DCHECK(OFFSET_OF(Node, class_id_) == Internals::kNodeClassIdOffset);
    DCHECK(OFFSET_OF(Node, flags_) == Internals::kNodeFlagsOffset);
49 50 51
    STATIC_ASSERT(static_cast<int>(NodeState::kMask) ==
                  Internals::kNodeStateMask);
    STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue);
52
    STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue);
53 54 55 56 57
    STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue);
    STATIC_ASSERT(static_cast<int>(IsIndependent::kShift) ==
                  Internals::kNodeIsIndependentShift);
    STATIC_ASSERT(static_cast<int>(IsPartiallyDependent::kShift) ==
                  Internals::kNodeIsPartiallyDependentShift);
58
  }
59

60
#ifdef ENABLE_HANDLE_ZAPPING
61 62
  ~Node() {
    // TODO(1428): if it's a weak handle we should have invoked its callback.
63
    // Zap the values for eager trapping.
64
    object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
65 66
    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
    index_ = 0;
67 68 69
    set_independent(false);
    set_partially_dependent(false);
    set_in_new_space_list(false);
70
    parameter_or_next_free_.next_free = NULL;
71
    weak_callback_ = NULL;
72
  }
73
#endif
74

75
  void Initialize(int index, Node** first_free) {
76
    index_ = static_cast<uint8_t>(index);
77
    DCHECK(static_cast<int>(index_) == index);
78 79
    set_state(FREE);
    set_in_new_space_list(false);
80 81
    parameter_or_next_free_.next_free = *first_free;
    *first_free = this;
82 83
  }

84
  void Acquire(Object* object) {
85
    DCHECK(state() == FREE);
86 87
    object_ = object;
    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
88 89 90
    set_independent(false);
    set_partially_dependent(false);
    set_state(NORMAL);
91
    parameter_or_next_free_.parameter = NULL;
92
    weak_callback_ = NULL;
93
    IncreaseBlockUses();
94 95
  }

96 97 98 99 100 101
  void Zap() {
    DCHECK(IsInUse());
    // Zap the values for eager trapping.
    object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
  }

102
  void Release() {
103
    DCHECK(IsInUse());
104
    set_state(FREE);
105
    // Zap the values for eager trapping.
106
    object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
107 108 109
    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
    set_independent(false);
    set_partially_dependent(false);
110
    weak_callback_ = NULL;
111
    DecreaseBlockUses();
112 113 114 115 116 117 118 119 120 121 122
  }

  // Object slot accessors.
  Object* object() const { return object_; }
  Object** location() { return &object_; }
  Handle<Object> handle() { return Handle<Object>(location()); }

  // Wrapper class ID accessors.
  bool has_wrapper_class_id() const {
    return class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId;
  }
123

124
  uint16_t wrapper_class_id() const { return class_id_; }
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139

  // State and flag accessors.

  State state() const {
    return NodeState::decode(flags_);
  }
  void set_state(State state) {
    flags_ = NodeState::update(flags_, state);
  }

  bool is_independent() {
    return IsIndependent::decode(flags_);
  }
  void set_independent(bool v) {
    flags_ = IsIndependent::update(flags_, v);
140 141
  }

142 143 144 145 146 147
  bool is_partially_dependent() {
    return IsPartiallyDependent::decode(flags_);
  }
  void set_partially_dependent(bool v) {
    flags_ = IsPartiallyDependent::update(flags_, v);
  }
148

149 150 151 152 153 154
  bool is_in_new_space_list() {
    return IsInNewSpaceList::decode(flags_);
  }
  void set_in_new_space_list(bool v) {
    flags_ = IsInNewSpaceList::update(flags_, v);
  }
155

156 157
  WeaknessType weakness_type() const {
    return NodeWeaknessType::decode(flags_);
158
  }
159 160
  void set_weakness_type(WeaknessType weakness_type) {
    flags_ = NodeWeaknessType::update(flags_, weakness_type);
161 162
  }

163 164
  bool IsNearDeath() const {
    // Check for PENDING to ensure correct answer when processing callbacks.
165
    return state() == PENDING || state() == NEAR_DEATH;
166 167
  }

168
  bool IsWeak() const { return state() == WEAK; }
169

170 171
  bool IsInUse() const { return state() != FREE; }

172 173 174 175
  bool IsRetainer() const {
    return state() != FREE &&
           !(state() == NEAR_DEATH && weakness_type() != NORMAL_WEAK);
  }
176

177
  bool IsStrongRetainer() const { return state() == NORMAL; }
178 179

  bool IsWeakRetainer() const {
180 181
    return state() == WEAK || state() == PENDING ||
           (state() == NEAR_DEATH && weakness_type() == NORMAL_WEAK);
182 183
  }

184
  void MarkPending() {
185
    DCHECK(state() == WEAK);
186
    set_state(PENDING);
187 188 189 190
  }

  // Independent flag accessors.
  void MarkIndependent() {
191
    DCHECK(IsInUse());
192
    set_independent(true);
193 194
  }

195
  void MarkPartiallyDependent() {
196
    DCHECK(IsInUse());
197
    if (GetGlobalHandles()->isolate()->heap()->InNewSpace(object_)) {
198
      set_partially_dependent(true);
199 200
    }
  }
201
  void clear_partially_dependent() { set_partially_dependent(false); }
202

203 204 205 206
  // Callback accessor.
  // TODO(svenpanne) Re-enable or nuke later.
  // WeakReferenceCallback callback() { return callback_; }

207 208
  // Callback parameter accessors.
  void set_parameter(void* parameter) {
209
    DCHECK(IsInUse());
210 211 212
    parameter_or_next_free_.parameter = parameter;
  }
  void* parameter() const {
213
    DCHECK(IsInUse());
214 215
    return parameter_or_next_free_.parameter;
  }
216 217 218

  // Accessors for next free node in the free list.
  Node* next_free() {
219
    DCHECK(state() == FREE);
220 221 222
    return parameter_or_next_free_.next_free;
  }
  void set_next_free(Node* value) {
223
    DCHECK(state() == FREE);
224 225 226
    parameter_or_next_free_.next_free = value;
  }

227
  void MakeWeak(void* parameter, WeakCallback weak_callback) {
228
    DCHECK(weak_callback != NULL);
229
    DCHECK(IsInUse());
230
    CHECK(object_ != NULL);
231
    set_state(WEAK);
232
    set_weakness_type(NORMAL_WEAK);
233
    set_parameter(parameter);
234
    weak_callback_ = weak_callback;
235 236
  }

237 238 239 240
  void MakeWeak(void* parameter,
                WeakCallbackInfo<void>::Callback phantom_callback,
                v8::WeakCallbackType type) {
    DCHECK(phantom_callback != nullptr);
241
    DCHECK(IsInUse());
242
    CHECK(object_ != nullptr);
243
    set_state(WEAK);
244 245 246 247 248
    switch (type) {
      case v8::WeakCallbackType::kParameter:
        set_weakness_type(PHANTOM_WEAK);
        break;
      case v8::WeakCallbackType::kInternalFields:
249
      set_weakness_type(PHANTOM_WEAK_2_INTERNAL_FIELDS);
250
      break;
251
    }
252
    set_parameter(parameter);
253 254 255
    weak_callback_ = reinterpret_cast<WeakCallback>(phantom_callback);
  }

256
  void* ClearWeakness() {
257
    DCHECK(IsInUse());
258
    void* p = parameter();
259
    set_state(NORMAL);
260
    set_parameter(NULL);
261
    return p;
262 263
  }

264
  void CollectPhantomCallbackData(
265 266
      Isolate* isolate,
      List<PendingPhantomCallback>* pending_phantom_callbacks) {
267 268 269 270 271 272 273 274 275 276 277 278 279
    DCHECK(weakness_type() == PHANTOM_WEAK ||
           weakness_type() == PHANTOM_WEAK_2_INTERNAL_FIELDS);
    DCHECK(state() == PENDING);

    void* internal_fields[v8::kInternalFieldsInWeakCallback] = {nullptr,
                                                                nullptr};
    if (weakness_type() != PHANTOM_WEAK && object()->IsJSObject()) {
      auto jsobject = JSObject::cast(object());
      int field_count = jsobject->GetInternalFieldCount();
      for (int i = 0; i < v8::kInternalFieldsInWeakCallback; ++i) {
        if (field_count == i) break;
        auto field = jsobject->GetInternalField(i);
        if (field->IsSmi()) internal_fields[i] = field;
280
      }
281
    }
282

283 284
    // Zap with something dangerous.
    *location() = reinterpret_cast<Object*>(0x6057ca11);
285

286 287 288 289 290 291
    typedef v8::WeakCallbackInfo<void> Data;
    auto callback = reinterpret_cast<Data::Callback>(weak_callback_);
    pending_phantom_callbacks->Add(
        PendingPhantomCallback(this, callback, parameter(), internal_fields));
    DCHECK(IsInUse());
    set_state(NEAR_DEATH);
292 293
  }

294
  bool PostGarbageCollectionProcessing(Isolate* isolate) {
295
    // Handles only weak handles (not phantom) that are dying.
296
    if (state() != Node::PENDING) return false;
297
    if (weak_callback_ == NULL) {
298
      Release();
299 300
      return false;
    }
301
    set_state(NEAR_DEATH);
302

303 304 305 306 307 308
    // Check that we are not passing a finalized external string to
    // the callback.
    DCHECK(!object_->IsExternalOneByteString() ||
           ExternalOneByteString::cast(object_)->resource() != NULL);
    DCHECK(!object_->IsExternalTwoByteString() ||
           ExternalTwoByteString::cast(object_)->resource() != NULL);
309 310
    if (weakness_type() != NORMAL_WEAK) return false;

311 312 313
    // Leaving V8.
    VMState<EXTERNAL> vmstate(isolate);
    HandleScope handle_scope(isolate);
314
    Object** object = location();
315 316 317 318 319 320 321
    Handle<Object> handle(*object, isolate);
    v8::WeakCallbackData<v8::Value, void> data(
        reinterpret_cast<v8::Isolate*>(isolate), parameter(),
        v8::Utils::ToLocal(handle));
    set_parameter(NULL);
    weak_callback_(data);

322
    // Absence of explicit cleanup or revival of weak handle
323
    // in most of the cases would lead to memory leak.
324
    CHECK(state() != NEAR_DEATH);
325
    return true;
326 327
  }

328 329
  inline GlobalHandles* GetGlobalHandles();

330 331
 private:
  inline NodeBlock* FindBlock();
332 333
  inline void IncreaseBlockUses();
  inline void DecreaseBlockUses();
334 335 336 337

  // Storage for object pointer.
  // Placed first to avoid offset computation.
  Object* object_;
338

339 340 341 342
  // Next word stores class_id, index, state, and independent.
  // Note: the most aligned fields should go first.

  // Wrapper class ID.
343 344
  uint16_t class_id_;

345 346 347
  // Index in the containing handle block.
  uint8_t index_;

348 349
  // This stores three flags (independent, partially_dependent and
  // in_new_space_list) and a State.
350 351 352 353 354
  class NodeState : public BitField<State, 0, 3> {};
  class IsIndependent : public BitField<bool, 3, 1> {};
  class IsPartiallyDependent : public BitField<bool, 4, 1> {};
  class IsInNewSpaceList : public BitField<bool, 5, 1> {};
  class NodeWeaknessType : public BitField<WeaknessType, 6, 2> {};
355

356
  uint8_t flags_;
357

358
  // Handle specific callback - might be a weak reference in disguise.
359
  WeakCallback weak_callback_;
360 361

  // Provided data for callback.  In FREE state, this is used for
362 363 364 365 366 367
  // the free list link.
  union {
    void* parameter;
    Node* next_free;
  } parameter_or_next_free_;

368 369 370
  DISALLOW_COPY_AND_ASSIGN(Node);
};

371

372
class GlobalHandles::NodeBlock {
373 374 375
 public:
  static const int kSize = 256;

376 377 378 379 380 381 382 383
  explicit NodeBlock(GlobalHandles* global_handles, NodeBlock* next)
      : next_(next),
        used_nodes_(0),
        next_used_(NULL),
        prev_used_(NULL),
        global_handles_(global_handles) {}

  void PutNodesOnFreeList(Node** first_free) {
384 385
    for (int i = kSize - 1; i >= 0; --i) {
      nodes_[i].Initialize(i, first_free);
386
    }
387
  }
388

389
  Node* node_at(int index) {
390
    DCHECK(0 <= index && index < kSize);
391 392 393 394
    return &nodes_[index];
  }

  void IncreaseUses() {
395
    DCHECK(used_nodes_ < kSize);
396 397 398 399 400 401 402
    if (used_nodes_++ == 0) {
      NodeBlock* old_first = global_handles_->first_used_block_;
      global_handles_->first_used_block_ = this;
      next_used_ = old_first;
      prev_used_ = NULL;
      if (old_first == NULL) return;
      old_first->prev_used_ = this;
403
    }
404
  }
405

406
  void DecreaseUses() {
407
    DCHECK(used_nodes_ > 0);
408 409 410 411 412 413
    if (--used_nodes_ == 0) {
      if (next_used_ != NULL) next_used_->prev_used_ = prev_used_;
      if (prev_used_ != NULL) prev_used_->next_used_ = next_used_;
      if (this == global_handles_->first_used_block_) {
        global_handles_->first_used_block_ = next_used_;
      }
414
    }
415
  }
416

417 418
  GlobalHandles* global_handles() { return global_handles_; }

419 420
  // Next block in the list of all blocks.
  NodeBlock* next() const { return next_; }
421

422 423 424
  // Next/previous block in the list of blocks with used nodes.
  NodeBlock* next_used() const { return next_used_; }
  NodeBlock* prev_used() const { return prev_used_; }
425

426 427
 private:
  Node nodes_[kSize];
428 429 430 431
  NodeBlock* const next_;
  int used_nodes_;
  NodeBlock* next_used_;
  NodeBlock* prev_used_;
432
  GlobalHandles* global_handles_;
433 434 435
};


436 437
GlobalHandles* GlobalHandles::Node::GetGlobalHandles() {
  return FindBlock()->global_handles();
438 439 440
}


441
GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() {
442 443 444
  intptr_t ptr = reinterpret_cast<intptr_t>(this);
  ptr = ptr - index_ * sizeof(Node);
  NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr);
445
  DCHECK(block->node_at(index_) == this);
446 447 448 449 450 451 452 453 454 455
  return block;
}


void GlobalHandles::Node::IncreaseBlockUses() {
  NodeBlock* node_block = FindBlock();
  node_block->IncreaseUses();
  GlobalHandles* global_handles = node_block->global_handles();
  global_handles->isolate()->counters()->global_handles()->Increment();
  global_handles->number_of_global_handles_++;
456 457 458
}


459 460 461 462 463 464 465 466
void GlobalHandles::Node::DecreaseBlockUses() {
  NodeBlock* node_block = FindBlock();
  GlobalHandles* global_handles = node_block->global_handles();
  parameter_or_next_free_.next_free = global_handles->first_free_;
  global_handles->first_free_ = this;
  node_block->DecreaseUses();
  global_handles->isolate()->counters()->global_handles()->Decrement();
  global_handles->number_of_global_handles_--;
467 468 469 470 471 472
}


class GlobalHandles::NodeIterator {
 public:
  explicit NodeIterator(GlobalHandles* global_handles)
473 474
      : block_(global_handles->first_used_block_),
        index_(0) {}
475

476
  bool done() const { return block_ == NULL; }
477 478

  Node* node() const {
479
    DCHECK(!done());
480
    return block_->node_at(index_);
481 482 483
  }

  void Advance() {
484
    DCHECK(!done());
485 486 487
    if (++index_ < NodeBlock::kSize) return;
    index_ = 0;
    block_ = block_->next_used();
488 489 490
  }

 private:
491 492
  NodeBlock* block_;
  int index_;
493 494

  DISALLOW_COPY_AND_ASSIGN(NodeIterator);
495 496 497
};


498 499
GlobalHandles::GlobalHandles(Isolate* isolate)
    : isolate_(isolate),
500
      number_of_global_handles_(0),
501 502 503
      first_block_(NULL),
      first_used_block_(NULL),
      first_free_(NULL),
504
      post_gc_processing_count_(0),
505
      object_group_connections_(kObjectGroupConnectionsCapacity) {}
506 507 508


GlobalHandles::~GlobalHandles() {
509 510 511 512 513
  NodeBlock* block = first_block_;
  while (block != NULL) {
    NodeBlock* tmp = block->next();
    delete block;
    block = tmp;
514
  }
515
  first_block_ = NULL;
516
}
517 518


519
Handle<Object> GlobalHandles::Create(Object* value) {
520 521 522 523
  if (first_free_ == NULL) {
    first_block_ = new NodeBlock(this, first_block_);
    first_block_->PutNodesOnFreeList(&first_free_);
  }
524
  DCHECK(first_free_ != NULL);
525 526 527 528
  // Take the first node in the free list.
  Node* result = first_free_;
  first_free_ = result->next_free();
  result->Acquire(value);
529 530 531 532
  if (isolate_->heap()->InNewSpace(value) &&
      !result->is_in_new_space_list()) {
    new_space_nodes_.Add(result);
    result->set_in_new_space_list(true);
533 534 535 536 537
  }
  return result->handle();
}


538
Handle<Object> GlobalHandles::CopyGlobal(Object** location) {
539
  DCHECK(location != NULL);
540 541 542 543
  return Node::FromLocation(location)->GetGlobalHandles()->Create(*location);
}


544
void GlobalHandles::Destroy(Object** location) {
545
  if (location != NULL) Node::FromLocation(location)->Release();
546 547 548
}


549
void GlobalHandles::MakeWeak(Object** location, void* parameter,
550 551 552 553 554
                             WeakCallback weak_callback) {
  Node::FromLocation(location)->MakeWeak(parameter, weak_callback);
}


555
typedef v8::WeakCallbackInfo<void>::Callback GenericCallback;
556 557


558 559 560 561
void GlobalHandles::MakeWeak(Object** location, void* parameter,
                             GenericCallback phantom_callback,
                             v8::WeakCallbackType type) {
  Node::FromLocation(location)->MakeWeak(parameter, phantom_callback, type);
562 563 564
}


565 566
void* GlobalHandles::ClearWeakness(Object** location) {
  return Node::FromLocation(location)->ClearWeakness();
567 568 569
}


570
void GlobalHandles::MarkIndependent(Object** location) {
571
  Node::FromLocation(location)->MarkIndependent();
572 573 574
}


575
void GlobalHandles::MarkPartiallyDependent(Object** location) {
576
  Node::FromLocation(location)->MarkPartiallyDependent();
577 578 579
}


580 581 582 583 584
bool GlobalHandles::IsIndependent(Object** location) {
  return Node::FromLocation(location)->is_independent();
}


585 586 587 588 589 590 591 592 593 594 595
bool GlobalHandles::IsNearDeath(Object** location) {
  return Node::FromLocation(location)->IsNearDeath();
}


bool GlobalHandles::IsWeak(Object** location) {
  return Node::FromLocation(location)->IsWeak();
}


void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
596
  for (NodeIterator it(this); !it.done(); it.Advance()) {
597 598
    Node* node = it.node();
    if (node->IsWeakRetainer()) {
599 600 601
      // Pending weak phantom handles die immediately. Everything else survives.
      if (node->state() == Node::PENDING &&
          node->weakness_type() != NORMAL_WEAK) {
602 603
          node->CollectPhantomCallbackData(isolate(),
                                           &pending_phantom_callbacks_);
604 605 606 607
      } else {
        v->VisitPointer(node->location());
      }
    }
608 609 610 611
  }
}


612
void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
613 614 615
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsWeak() && f(it.node()->location())) {
      it.node()->MarkPending();
616 617 618 619 620
    }
  }
}


621 622 623 624
void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
    if (node->IsStrongRetainer() ||
625 626 627
        (node->IsWeakRetainer() && !node->is_independent() &&
         !node->is_partially_dependent())) {
        v->VisitPointer(node->location());
628 629 630 631 632
    }
  }
}


633 634 635 636
void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles(
    WeakSlotCallbackWithHeap f) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
637
    DCHECK(node->is_in_new_space_list());
638 639
    if ((node->is_independent() || node->is_partially_dependent()) &&
        node->IsWeak() && f(isolate_->heap(), node->location())) {
640
      node->MarkPending();
641 642 643 644 645
    }
  }
}


646 647 648
void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
649
    DCHECK(node->is_in_new_space_list());
650 651
    if ((node->is_independent() || node->is_partially_dependent()) &&
        node->IsWeakRetainer()) {
652 653 654
      // Pending weak phantom handles die immediately. Everything else survives.
      if (node->state() == Node::PENDING &&
          node->weakness_type() != NORMAL_WEAK) {
655 656
        node->CollectPhantomCallbackData(isolate(),
                                         &pending_phantom_callbacks_);
657 658
      } else {
        v->VisitPointer(node->location());
659
      }
660 661 662 663 664
    }
  }
}


665 666
bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
                                        WeakSlotCallbackWithHeap can_skip) {
667
  ComputeObjectGroupsAndImplicitReferences();
668
  int last = 0;
669
  bool any_group_was_visited = false;
670 671
  for (int i = 0; i < object_groups_.length(); i++) {
    ObjectGroup* entry = object_groups_.at(i);
672
    DCHECK(entry != NULL);
673

674
    Object*** objects = entry->objects;
675
    bool group_should_be_visited = false;
676
    for (size_t j = 0; j < entry->length; j++) {
677 678 679 680 681
      Object* object = *objects[j];
      if (object->IsHeapObject()) {
        if (!can_skip(isolate_->heap(), &object)) {
          group_should_be_visited = true;
          break;
682 683
        }
      }
684
    }
685

686 687 688 689
    if (!group_should_be_visited) {
      object_groups_[last++] = entry;
      continue;
    }
690

691 692
    // An object in the group requires visiting, so iterate over all
    // objects in the group.
693
    for (size_t j = 0; j < entry->length; ++j) {
694 695 696 697
      Object* object = *objects[j];
      if (object->IsHeapObject()) {
        v->VisitPointer(&object);
        any_group_was_visited = true;
698 699
      }
    }
700 701 702

    // Once the entire group has been iterated over, set the object
    // group to NULL so it won't be processed again.
703
    delete entry;
704
    object_groups_.at(i) = NULL;
705
  }
706
  object_groups_.Rewind(last);
707 708 709 710
  return any_group_was_visited;
}


711 712
int GlobalHandles::PostScavengeProcessing(
    const int initial_post_gc_processing_count) {
713
  int freed_nodes = 0;
714 715 716 717 718 719 720
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
    DCHECK(node->is_in_new_space_list());
    if (!node->IsRetainer()) {
      // Free nodes do not have weak callbacks. Do not use them to compute
      // the freed_nodes.
      continue;
721
    }
722 723 724 725 726 727 728 729 730 731 732 733 734 735
    // Skip dependent handles. Their weak callbacks might expect to be
    // called between two global garbage collection callbacks which
    // are not called for minor collections.
    if (!node->is_independent() && !node->is_partially_dependent()) {
      continue;
    }
    node->clear_partially_dependent();
    if (node->PostGarbageCollectionProcessing(isolate_)) {
      if (initial_post_gc_processing_count != post_gc_processing_count_) {
        // Weak callback triggered another GC and another round of
        // PostGarbageCollection processing.  The current node might
        // have been deleted in that round, so we need to bail out (or
        // restart the processing).
        return freed_nodes;
736
      }
737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
    }
    if (!node->IsRetainer()) {
      freed_nodes++;
    }
  }
  return freed_nodes;
}


int GlobalHandles::PostMarkSweepProcessing(
    const int initial_post_gc_processing_count) {
  int freed_nodes = 0;
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (!it.node()->IsRetainer()) {
      // Free nodes do not have weak callbacks. Do not use them to compute
      // the freed_nodes.
      continue;
    }
    it.node()->clear_partially_dependent();
    if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
      if (initial_post_gc_processing_count != post_gc_processing_count_) {
        // See the comment above.
        return freed_nodes;
760
      }
761
    }
762 763 764
    if (!it.node()->IsRetainer()) {
      freed_nodes++;
    }
765
  }
766 767 768 769 770
  return freed_nodes;
}


void GlobalHandles::UpdateListOfNewSpaceNodes() {
771 772 773
  int last = 0;
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
774
    DCHECK(node->is_in_new_space_list());
775 776 777
    if (node->IsRetainer()) {
      if (isolate_->heap()->InNewSpace(node->object())) {
        new_space_nodes_[last++] = node;
778
        isolate_->heap()->IncrementNodesCopiedInNewSpace();
779 780
      } else {
        node->set_in_new_space_list(false);
781
        isolate_->heap()->IncrementNodesPromoted();
782
      }
783 784
    } else {
      node->set_in_new_space_list(false);
785
      isolate_->heap()->IncrementNodesDiedInNewSpace();
786
    }
787
  }
788
  new_space_nodes_.Rewind(last);
789 790 791 792 793
}


int GlobalHandles::DispatchPendingPhantomCallbacks() {
  int freed_nodes = 0;
dcarney's avatar
dcarney committed
794 795 796 797 798 799 800 801 802 803 804 805
  {
    // The initial pass callbacks must simply clear the nodes.
    for (auto i = pending_phantom_callbacks_.begin();
         i != pending_phantom_callbacks_.end(); ++i) {
      auto callback = i;
      // Skip callbacks that have already been processed once.
      if (callback->node() == nullptr) continue;
      callback->Invoke(isolate());
      freed_nodes++;
    }
  }
  // The second pass empties the list.
806
  while (pending_phantom_callbacks_.length() != 0) {
dcarney's avatar
dcarney committed
807 808 809 810 811 812
    auto callback = pending_phantom_callbacks_.RemoveLast();
    DCHECK(callback.node() == nullptr);
    // No second pass callback required.
    if (callback.callback() == nullptr) continue;
    // Fire second pass callback.
    callback.Invoke(isolate());
813 814 815 816 817
  }
  return freed_nodes;
}


dcarney's avatar
dcarney committed
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
void GlobalHandles::PendingPhantomCallback::Invoke(Isolate* isolate) {
  Data::Callback* callback_addr = nullptr;
  if (node_ != nullptr) {
    // Initialize for first pass callback.
    DCHECK(node_->state() == Node::NEAR_DEATH);
    callback_addr = &callback_;
  }
  Data data(reinterpret_cast<v8::Isolate*>(isolate), parameter_,
            internal_fields_, callback_addr);
  Data::Callback callback = callback_;
  callback_ = nullptr;
  callback(data);
  if (node_ != nullptr) {
    // Transition to second pass state.
    DCHECK(node_->state() == Node::FREE);
    node_ = nullptr;
  }
}


838 839 840 841 842 843 844
int GlobalHandles::PostGarbageCollectionProcessing(GarbageCollector collector) {
  // Process weak global handle callbacks. This must be done after the
  // GC is completely done, because the callbacks may invoke arbitrary
  // API functions.
  DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
  const int initial_post_gc_processing_count = ++post_gc_processing_count_;
  int freed_nodes = 0;
845 846 847 848 849 850
  freed_nodes += DispatchPendingPhantomCallbacks();
  if (initial_post_gc_processing_count != post_gc_processing_count_) {
    // If the callbacks caused a nested GC, then return.  See comment in
    // PostScavengeProcessing.
    return freed_nodes;
  }
851 852 853 854 855 856 857 858 859 860 861 862 863
  if (collector == SCAVENGER) {
    freed_nodes = PostScavengeProcessing(initial_post_gc_processing_count);
  } else {
    freed_nodes = PostMarkSweepProcessing(initial_post_gc_processing_count);
  }
  if (initial_post_gc_processing_count != post_gc_processing_count_) {
    // If the callbacks caused a nested GC, then return.  See comment in
    // PostScavengeProcessing.
    return freed_nodes;
  }
  if (initial_post_gc_processing_count == post_gc_processing_count_) {
    UpdateListOfNewSpaceNodes();
  }
864
  return freed_nodes;
865 866 867
}


868
void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
869 870 871
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsStrongRetainer()) {
      v->VisitPointer(it.node()->location());
872 873 874 875
    }
  }
}

876

877
void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
878 879 880
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsRetainer()) {
      v->VisitPointer(it.node()->location());
881 882 883 884 885
    }
  }
}


886
void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
887
  for (NodeIterator it(this); !it.done(); it.Advance()) {
888
    if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) {
889 890
      v->VisitEmbedderReference(it.node()->location(),
                                it.node()->wrapper_class_id());
891 892 893 894 895
    }
  }
}


896 897 898 899 900 901 902 903 904 905 906
void GlobalHandles::IterateAllRootsInNewSpaceWithClassIds(ObjectVisitor* v) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
    if (node->IsRetainer() && node->has_wrapper_class_id()) {
      v->VisitEmbedderReference(node->location(),
                                node->wrapper_class_id());
    }
  }
}


907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
int GlobalHandles::NumberOfWeakHandles() {
  int count = 0;
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsWeakRetainer()) {
      count++;
    }
  }
  return count;
}


int GlobalHandles::NumberOfGlobalObjectWeakHandles() {
  int count = 0;
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsWeakRetainer() &&
        it.node()->object()->IsJSGlobalObject()) {
      count++;
    }
  }
  return count;
}


930
void GlobalHandles::RecordStats(HeapStats* stats) {
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
  *stats->global_handle_count = 0;
  *stats->weak_global_handle_count = 0;
  *stats->pending_global_handle_count = 0;
  *stats->near_death_global_handle_count = 0;
  *stats->free_global_handle_count = 0;
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    *stats->global_handle_count += 1;
    if (it.node()->state() == Node::WEAK) {
      *stats->weak_global_handle_count += 1;
    } else if (it.node()->state() == Node::PENDING) {
      *stats->pending_global_handle_count += 1;
    } else if (it.node()->state() == Node::NEAR_DEATH) {
      *stats->near_death_global_handle_count += 1;
    } else if (it.node()->state() == Node::FREE) {
      *stats->free_global_handle_count += 1;
    }
  }
948 949
}

950 951 952
#ifdef DEBUG

void GlobalHandles::PrintStats() {
953 954 955 956 957 958 959 960 961 962 963 964 965 966
  int total = 0;
  int weak = 0;
  int pending = 0;
  int near_death = 0;
  int destroyed = 0;

  for (NodeIterator it(this); !it.done(); it.Advance()) {
    total++;
    if (it.node()->state() == Node::WEAK) weak++;
    if (it.node()->state() == Node::PENDING) pending++;
    if (it.node()->state() == Node::NEAR_DEATH) near_death++;
    if (it.node()->state() == Node::FREE) destroyed++;
  }

967
  PrintF("Global Handle Statistics:\n");
968 969 970 971 972
  PrintF("  allocated memory = %" V8_PTR_PREFIX "dB\n", sizeof(Node) * total);
  PrintF("  # weak       = %d\n", weak);
  PrintF("  # pending    = %d\n", pending);
  PrintF("  # near_death = %d\n", near_death);
  PrintF("  # free       = %d\n", destroyed);
973 974 975
  PrintF("  # total      = %d\n", total);
}

976

977 978
void GlobalHandles::Print() {
  PrintF("Global handles:\n");
979 980 981 982 983
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    PrintF("  handle %p to %p%s\n",
           reinterpret_cast<void*>(it.node()->location()),
           reinterpret_cast<void*>(it.node()->object()),
           it.node()->IsWeak() ? " (weak)" : "");
984 985 986 987 988 989
  }
}

#endif


990 991 992 993

void GlobalHandles::AddObjectGroup(Object*** handles,
                                   size_t length,
                                   v8::RetainedObjectInfo* info) {
994 995
#ifdef DEBUG
  for (size_t i = 0; i < length; ++i) {
996
    DCHECK(!Node::FromLocation(handles[i])->is_independent());
997 998
  }
#endif
999 1000 1001
  if (length == 0) {
    if (info != NULL) info->Dispose();
    return;
1002
  }
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
  ObjectGroup* group = new ObjectGroup(length);
  for (size_t i = 0; i < length; ++i)
    group->objects[i] = handles[i];
  group->info = info;
  object_groups_.Add(group);
}


void GlobalHandles::SetObjectGroupId(Object** handle,
                                     UniqueId id) {
  object_group_connections_.Add(ObjectGroupConnection(id, handle));
}


void GlobalHandles::SetRetainedObjectInfo(UniqueId id,
                                          RetainedObjectInfo* info) {
  retainer_infos_.Add(ObjectGroupRetainerInfo(id, info));
1020 1021
}

1022

1023
void GlobalHandles::SetReferenceFromGroup(UniqueId id, Object** child) {
1024
  DCHECK(!Node::FromLocation(child)->is_independent());
1025 1026 1027 1028 1029
  implicit_ref_connections_.Add(ObjectGroupConnection(id, child));
}


void GlobalHandles::SetReference(HeapObject** parent, Object** child) {
1030
  DCHECK(!Node::FromLocation(child)->is_independent());
1031 1032 1033
  ImplicitRefGroup* group = new ImplicitRefGroup(parent, 1);
  group->children[0] = child;
  implicit_ref_groups_.Add(group);
1034 1035 1036
}


1037
void GlobalHandles::RemoveObjectGroups() {
1038 1039
  for (int i = 0; i < object_groups_.length(); i++)
    delete object_groups_.at(i);
1040
  object_groups_.Clear();
1041 1042 1043 1044 1045
  for (int i = 0; i < retainer_infos_.length(); ++i)
    retainer_infos_[i].info->Dispose();
  retainer_infos_.Clear();
  object_group_connections_.Clear();
  object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
1046 1047
}

1048 1049

void GlobalHandles::RemoveImplicitRefGroups() {
1050
  for (int i = 0; i < implicit_ref_groups_.length(); i++) {
1051
    delete implicit_ref_groups_.at(i);
1052
  }
1053
  implicit_ref_groups_.Clear();
1054
  implicit_ref_connections_.Clear();
1055 1056 1057
}


1058 1059 1060 1061 1062
void GlobalHandles::TearDown() {
  // TODO(1428): invoke weak callbacks.
}


1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
void GlobalHandles::ComputeObjectGroupsAndImplicitReferences() {
  if (object_group_connections_.length() == 0) {
    for (int i = 0; i < retainer_infos_.length(); ++i)
      retainer_infos_[i].info->Dispose();
    retainer_infos_.Clear();
    implicit_ref_connections_.Clear();
    return;
  }

  object_group_connections_.Sort();
  retainer_infos_.Sort();
  implicit_ref_connections_.Sort();

  int info_index = 0;  // For iterating retainer_infos_.
  UniqueId current_group_id(0);
  int current_group_start = 0;

  int current_implicit_refs_start = 0;
  int current_implicit_refs_end = 0;
  for (int i = 0; i <= object_group_connections_.length(); ++i) {
    if (i == 0)
      current_group_id = object_group_connections_[i].id;
    if (i == object_group_connections_.length() ||
        current_group_id != object_group_connections_[i].id) {
      // Group detected: objects in indices [current_group_start, i[.

      // Find out which implicit references are related to this group. (We want
      // to ignore object groups which only have 1 object, but that object is
      // needed as a representative object for the implicit refrerence group.)
      while (current_implicit_refs_start < implicit_ref_connections_.length() &&
             implicit_ref_connections_[current_implicit_refs_start].id <
                 current_group_id)
        ++current_implicit_refs_start;
      current_implicit_refs_end = current_implicit_refs_start;
      while (current_implicit_refs_end < implicit_ref_connections_.length() &&
             implicit_ref_connections_[current_implicit_refs_end].id ==
                 current_group_id)
        ++current_implicit_refs_end;

      if (current_implicit_refs_end > current_implicit_refs_start) {
        // Find a representative object for the implicit references.
        HeapObject** representative = NULL;
        for (int j = current_group_start; j < i; ++j) {
          Object** object = object_group_connections_[j].object;
          if ((*object)->IsHeapObject()) {
            representative = reinterpret_cast<HeapObject**>(object);
            break;
          }
        }
        if (representative) {
          ImplicitRefGroup* group = new ImplicitRefGroup(
              representative,
              current_implicit_refs_end - current_implicit_refs_start);
          for (int j = current_implicit_refs_start;
               j < current_implicit_refs_end;
               ++j) {
            group->children[j - current_implicit_refs_start] =
                implicit_ref_connections_[j].object;
          }
          implicit_ref_groups_.Add(group);
        }
        current_implicit_refs_start = current_implicit_refs_end;
      }

      // Find a RetainedObjectInfo for the group.
      RetainedObjectInfo* info = NULL;
      while (info_index < retainer_infos_.length() &&
             retainer_infos_[info_index].id < current_group_id) {
        retainer_infos_[info_index].info->Dispose();
        ++info_index;
      }
      if (info_index < retainer_infos_.length() &&
          retainer_infos_[info_index].id == current_group_id) {
        // This object group has an associated ObjectGroupRetainerInfo.
        info = retainer_infos_[info_index].info;
        ++info_index;
      }

      // Ignore groups which only contain one object.
      if (i > current_group_start + 1) {
        ObjectGroup* group = new ObjectGroup(i - current_group_start);
        for (int j = current_group_start; j < i; ++j) {
          group->objects[j - current_group_start] =
              object_group_connections_[j].object;
        }
        group->info = info;
        object_groups_.Add(group);
      } else if (info) {
        info->Dispose();
      }

      if (i < object_group_connections_.length()) {
        current_group_id = object_group_connections_[i].id;
        current_group_start = i;
      }
    }
  }
  object_group_connections_.Clear();
  object_group_connections_.Initialize(kObjectGroupConnectionsCapacity);
  retainer_infos_.Clear();
  implicit_ref_connections_.Clear();
}


1167
EternalHandles::EternalHandles() : size_(0) {
1168
  for (unsigned i = 0; i < arraysize(singleton_handles_); i++) {
1169 1170 1171 1172 1173 1174
    singleton_handles_[i] = kInvalidIndex;
  }
}


EternalHandles::~EternalHandles() {
1175
  for (int i = 0; i < blocks_.length(); i++) delete[] blocks_[i];
1176 1177 1178 1179 1180 1181
}


void EternalHandles::IterateAllRoots(ObjectVisitor* visitor) {
  int limit = size_;
  for (int i = 0; i < blocks_.length(); i++) {
1182
    DCHECK(limit > 0);
1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
    Object** block = blocks_[i];
    visitor->VisitPointers(block, block + Min(limit, kSize));
    limit -= kSize;
  }
}


void EternalHandles::IterateNewSpaceRoots(ObjectVisitor* visitor) {
  for (int i = 0; i < new_space_indices_.length(); i++) {
    visitor->VisitPointer(GetLocation(new_space_indices_[i]));
  }
}


void EternalHandles::PostGarbageCollectionProcessing(Heap* heap) {
  int last = 0;
  for (int i = 0; i < new_space_indices_.length(); i++) {
    int index = new_space_indices_[i];
    if (heap->InNewSpace(*GetLocation(index))) {
      new_space_indices_[last++] = index;
    }
  }
  new_space_indices_.Rewind(last);
}


1209
void EternalHandles::Create(Isolate* isolate, Object* object, int* index) {
1210
  DCHECK_EQ(kInvalidIndex, *index);
1211
  if (object == NULL) return;
1212
  DCHECK_NE(isolate->heap()->the_hole_value(), object);
1213 1214 1215 1216 1217 1218 1219 1220 1221
  int block = size_ >> kShift;
  int offset = size_ & kMask;
  // need to resize
  if (offset == 0) {
    Object** next_block = new Object*[kSize];
    Object* the_hole = isolate->heap()->the_hole_value();
    MemsetPointer(next_block, the_hole, kSize);
    blocks_.Add(next_block);
  }
1222
  DCHECK_EQ(isolate->heap()->the_hole_value(), blocks_[block][offset]);
1223 1224 1225 1226
  blocks_[block][offset] = object;
  if (isolate->heap()->InNewSpace(object)) {
    new_space_indices_.Add(size_);
  }
1227
  *index = size_++;
1228 1229 1230
}


1231
} }  // namespace v8::internal