global-handles.cc 30.6 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
    NORMAL,     // Normal global handle.
    WEAK,       // Flagged as weak but not yet finalized.
    PENDING,    // Has been recognized as only reachable by weak handles.
36
    NEAR_DEATH  // Callback has informed the handle is near death.
37
  };
38

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

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

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

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

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

95
  void Release() {
96
    DCHECK(state() != FREE);
97
    set_state(FREE);
98
    // Zap the values for eager trapping.
99
    object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue);
100 101 102
    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
    set_independent(false);
    set_partially_dependent(false);
103
    weak_callback_ = NULL;
104
    DecreaseBlockUses();
105 106 107 108 109 110 111 112 113 114 115
  }

  // 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;
  }
116

117
  uint16_t wrapper_class_id() const { return class_id_; }
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

  // 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);
133 134
  }

135 136 137 138 139 140
  bool is_partially_dependent() {
    return IsPartiallyDependent::decode(flags_);
  }
  void set_partially_dependent(bool v) {
    flags_ = IsPartiallyDependent::update(flags_, v);
  }
141

142 143 144 145 146 147
  bool is_in_new_space_list() {
    return IsInNewSpaceList::decode(flags_);
  }
  void set_in_new_space_list(bool v) {
    flags_ = IsInNewSpaceList::update(flags_, v);
  }
148 149 150

  bool IsNearDeath() const {
    // Check for PENDING to ensure correct answer when processing callbacks.
151
    return state() == PENDING || state() == NEAR_DEATH;
152 153
  }

154
  bool IsWeak() const { return state() == WEAK; }
155

156
  bool IsRetainer() const { return state() != FREE; }
157

158
  bool IsStrongRetainer() const { return state() == NORMAL; }
159 160

  bool IsWeakRetainer() const {
161
    return state() == WEAK || state() == PENDING || state() == NEAR_DEATH;
162 163
  }

164
  void MarkPending() {
165
    DCHECK(state() == WEAK);
166
    set_state(PENDING);
167 168 169 170
  }

  // Independent flag accessors.
  void MarkIndependent() {
171
    DCHECK(state() != FREE);
172
    set_independent(true);
173 174
  }

175
  void MarkPartiallyDependent() {
176
    DCHECK(state() != FREE);
177
    if (GetGlobalHandles()->isolate()->heap()->InNewSpace(object_)) {
178
      set_partially_dependent(true);
179 180
    }
  }
181
  void clear_partially_dependent() { set_partially_dependent(false); }
182

183 184 185 186
  // Callback accessor.
  // TODO(svenpanne) Re-enable or nuke later.
  // WeakReferenceCallback callback() { return callback_; }

187 188
  // Callback parameter accessors.
  void set_parameter(void* parameter) {
189
    DCHECK(state() != FREE);
190 191 192
    parameter_or_next_free_.parameter = parameter;
  }
  void* parameter() const {
193
    DCHECK(state() != FREE);
194 195
    return parameter_or_next_free_.parameter;
  }
196 197 198

  // Accessors for next free node in the free list.
  Node* next_free() {
199
    DCHECK(state() == FREE);
200 201 202
    return parameter_or_next_free_.next_free;
  }
  void set_next_free(Node* value) {
203
    DCHECK(state() == FREE);
204 205 206
    parameter_or_next_free_.next_free = value;
  }

207
  void MakeWeak(void* parameter, WeakCallback weak_callback) {
208 209
    DCHECK(weak_callback != NULL);
    DCHECK(state() != FREE);
210
    CHECK(object_ != NULL);
211
    set_state(WEAK);
212
    set_parameter(parameter);
213
    weak_callback_ = weak_callback;
214 215
  }

216
  void* ClearWeakness() {
217
    DCHECK(state() != FREE);
218
    void* p = parameter();
219
    set_state(NORMAL);
220
    set_parameter(NULL);
221
    return p;
222 223
  }

224
  bool PostGarbageCollectionProcessing(Isolate* isolate) {
225
    if (state() != Node::PENDING) return false;
226
    if (weak_callback_ == NULL) {
227
      Release();
228 229
      return false;
    }
230
    void* par = parameter();
231
    set_state(NEAR_DEATH);
232
    set_parameter(NULL);
233

234
    Object** object = location();
235
    {
236 237
      // Check that we are not passing a finalized external string to
      // the callback.
238 239
      DCHECK(!object_->IsExternalOneByteString() ||
             ExternalOneByteString::cast(object_)->resource() != NULL);
240
      DCHECK(!object_->IsExternalTwoByteString() ||
241
             ExternalTwoByteString::cast(object_)->resource() != NULL);
242
      // Leaving V8.
243
      VMState<EXTERNAL> state(isolate);
244
      HandleScope handle_scope(isolate);
245 246 247 248 249 250
      Handle<Object> handle(*object, isolate);
      v8::WeakCallbackData<v8::Value, void> data(
          reinterpret_cast<v8::Isolate*>(isolate),
          v8::Utils::ToLocal(handle),
          par);
      weak_callback_(data);
251
    }
252
    // Absence of explicit cleanup or revival of weak handle
253
    // in most of the cases would lead to memory leak.
254
    CHECK(state() != NEAR_DEATH);
255
    return true;
256 257
  }

258 259
  inline GlobalHandles* GetGlobalHandles();

260 261
 private:
  inline NodeBlock* FindBlock();
262 263
  inline void IncreaseBlockUses();
  inline void DecreaseBlockUses();
264 265 266 267

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

269 270 271 272
  // Next word stores class_id, index, state, and independent.
  // Note: the most aligned fields should go first.

  // Wrapper class ID.
273 274
  uint16_t class_id_;

275 276 277
  // Index in the containing handle block.
  uint8_t index_;

278 279 280 281 282 283
  // This stores three flags (independent, partially_dependent and
  // in_new_space_list) and a State.
  class NodeState:            public BitField<State, 0, 4> {};
  class IsIndependent:        public BitField<bool,  4, 1> {};
  class IsPartiallyDependent: public BitField<bool,  5, 1> {};
  class IsInNewSpaceList:     public BitField<bool,  6, 1> {};
284

285
  uint8_t flags_;
286

287
  // Handle specific callback - might be a weak reference in disguise.
288
  WeakCallback weak_callback_;
289 290

  // Provided data for callback.  In FREE state, this is used for
291 292 293 294 295 296
  // the free list link.
  union {
    void* parameter;
    Node* next_free;
  } parameter_or_next_free_;

297 298 299
  DISALLOW_COPY_AND_ASSIGN(Node);
};

300

301
class GlobalHandles::NodeBlock {
302 303 304
 public:
  static const int kSize = 256;

305 306 307 308 309 310 311 312
  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) {
313 314
    for (int i = kSize - 1; i >= 0; --i) {
      nodes_[i].Initialize(i, first_free);
315
    }
316
  }
317

318
  Node* node_at(int index) {
319
    DCHECK(0 <= index && index < kSize);
320 321 322 323
    return &nodes_[index];
  }

  void IncreaseUses() {
324
    DCHECK(used_nodes_ < kSize);
325 326 327 328 329 330 331
    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;
332
    }
333
  }
334

335
  void DecreaseUses() {
336
    DCHECK(used_nodes_ > 0);
337 338 339 340 341 342
    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_;
      }
343
    }
344
  }
345

346 347
  GlobalHandles* global_handles() { return global_handles_; }

348 349
  // Next block in the list of all blocks.
  NodeBlock* next() const { return next_; }
350

351 352 353
  // 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_; }
354

355 356
 private:
  Node nodes_[kSize];
357 358 359 360
  NodeBlock* const next_;
  int used_nodes_;
  NodeBlock* next_used_;
  NodeBlock* prev_used_;
361
  GlobalHandles* global_handles_;
362 363 364
};


365 366
GlobalHandles* GlobalHandles::Node::GetGlobalHandles() {
  return FindBlock()->global_handles();
367 368 369
}


370
GlobalHandles::NodeBlock* GlobalHandles::Node::FindBlock() {
371 372 373
  intptr_t ptr = reinterpret_cast<intptr_t>(this);
  ptr = ptr - index_ * sizeof(Node);
  NodeBlock* block = reinterpret_cast<NodeBlock*>(ptr);
374
  DCHECK(block->node_at(index_) == this);
375 376 377 378 379 380 381 382 383 384
  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_++;
385 386 387
}


388 389 390 391 392 393 394 395
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_--;
396 397 398 399 400 401
}


class GlobalHandles::NodeIterator {
 public:
  explicit NodeIterator(GlobalHandles* global_handles)
402 403
      : block_(global_handles->first_used_block_),
        index_(0) {}
404

405
  bool done() const { return block_ == NULL; }
406 407

  Node* node() const {
408
    DCHECK(!done());
409
    return block_->node_at(index_);
410 411 412
  }

  void Advance() {
413
    DCHECK(!done());
414 415 416
    if (++index_ < NodeBlock::kSize) return;
    index_ = 0;
    block_ = block_->next_used();
417 418 419
  }

 private:
420 421
  NodeBlock* block_;
  int index_;
422 423

  DISALLOW_COPY_AND_ASSIGN(NodeIterator);
424 425 426
};


427 428
GlobalHandles::GlobalHandles(Isolate* isolate)
    : isolate_(isolate),
429
      number_of_global_handles_(0),
430 431 432
      first_block_(NULL),
      first_used_block_(NULL),
      first_free_(NULL),
433
      post_gc_processing_count_(0),
434
      object_group_connections_(kObjectGroupConnectionsCapacity) {}
435 436 437


GlobalHandles::~GlobalHandles() {
438 439 440 441 442
  NodeBlock* block = first_block_;
  while (block != NULL) {
    NodeBlock* tmp = block->next();
    delete block;
    block = tmp;
443
  }
444
  first_block_ = NULL;
445
}
446 447


448
Handle<Object> GlobalHandles::Create(Object* value) {
449 450 451 452
  if (first_free_ == NULL) {
    first_block_ = new NodeBlock(this, first_block_);
    first_block_->PutNodesOnFreeList(&first_free_);
  }
453
  DCHECK(first_free_ != NULL);
454 455 456 457
  // Take the first node in the free list.
  Node* result = first_free_;
  first_free_ = result->next_free();
  result->Acquire(value);
458 459 460 461
  if (isolate_->heap()->InNewSpace(value) &&
      !result->is_in_new_space_list()) {
    new_space_nodes_.Add(result);
    result->set_in_new_space_list(true);
462 463 464 465 466
  }
  return result->handle();
}


467
Handle<Object> GlobalHandles::CopyGlobal(Object** location) {
468
  DCHECK(location != NULL);
469 470 471 472
  return Node::FromLocation(location)->GetGlobalHandles()->Create(*location);
}


473
void GlobalHandles::Destroy(Object** location) {
474
  if (location != NULL) Node::FromLocation(location)->Release();
475 476 477
}


478 479
void GlobalHandles::MakeWeak(Object** location,
                             void* parameter,
480 481
                             WeakCallback weak_callback) {
  Node::FromLocation(location)->MakeWeak(parameter, weak_callback);
482 483 484
}


485 486
void* GlobalHandles::ClearWeakness(Object** location) {
  return Node::FromLocation(location)->ClearWeakness();
487 488 489
}


490
void GlobalHandles::MarkIndependent(Object** location) {
491
  Node::FromLocation(location)->MarkIndependent();
492 493 494
}


495
void GlobalHandles::MarkPartiallyDependent(Object** location) {
496
  Node::FromLocation(location)->MarkPartiallyDependent();
497 498 499
}


500 501 502 503 504
bool GlobalHandles::IsIndependent(Object** location) {
  return Node::FromLocation(location)->is_independent();
}


505 506 507 508 509 510 511 512 513 514 515
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) {
516 517
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsWeakRetainer()) v->VisitPointer(it.node()->location());
518 519 520 521
  }
}


522
void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
523 524 525
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsWeak() && f(it.node()->location())) {
      it.node()->MarkPending();
526 527 528 529 530
    }
  }
}


531 532 533 534
void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(ObjectVisitor* v) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
    if (node->IsStrongRetainer() ||
535 536 537
        (node->IsWeakRetainer() && !node->is_independent() &&
         !node->is_partially_dependent())) {
        v->VisitPointer(node->location());
538 539 540 541 542
    }
  }
}


543 544 545 546
void GlobalHandles::IdentifyNewSpaceWeakIndependentHandles(
    WeakSlotCallbackWithHeap f) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
547
    DCHECK(node->is_in_new_space_list());
548 549
    if ((node->is_independent() || node->is_partially_dependent()) &&
        node->IsWeak() && f(isolate_->heap(), node->location())) {
550
      node->MarkPending();
551 552 553 554 555
    }
  }
}


556 557 558
void GlobalHandles::IterateNewSpaceWeakIndependentRoots(ObjectVisitor* v) {
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
559
    DCHECK(node->is_in_new_space_list());
560 561
    if ((node->is_independent() || node->is_partially_dependent()) &&
        node->IsWeakRetainer()) {
562 563 564 565 566 567
      v->VisitPointer(node->location());
    }
  }
}


568 569
bool GlobalHandles::IterateObjectGroups(ObjectVisitor* v,
                                        WeakSlotCallbackWithHeap can_skip) {
570
  ComputeObjectGroupsAndImplicitReferences();
571
  int last = 0;
572
  bool any_group_was_visited = false;
573 574
  for (int i = 0; i < object_groups_.length(); i++) {
    ObjectGroup* entry = object_groups_.at(i);
575
    DCHECK(entry != NULL);
576

577
    Object*** objects = entry->objects;
578
    bool group_should_be_visited = false;
579
    for (size_t j = 0; j < entry->length; j++) {
580 581 582 583 584
      Object* object = *objects[j];
      if (object->IsHeapObject()) {
        if (!can_skip(isolate_->heap(), &object)) {
          group_should_be_visited = true;
          break;
585 586
        }
      }
587
    }
588

589 590 591 592
    if (!group_should_be_visited) {
      object_groups_[last++] = entry;
      continue;
    }
593

594 595
    // An object in the group requires visiting, so iterate over all
    // objects in the group.
596
    for (size_t j = 0; j < entry->length; ++j) {
597 598 599 600
      Object* object = *objects[j];
      if (object->IsHeapObject()) {
        v->VisitPointer(&object);
        any_group_was_visited = true;
601 602
      }
    }
603 604 605

    // Once the entire group has been iterated over, set the object
    // group to NULL so it won't be processed again.
606
    delete entry;
607
    object_groups_.at(i) = NULL;
608
  }
609
  object_groups_.Rewind(last);
610 611 612 613
  return any_group_was_visited;
}


614
int GlobalHandles::PostGarbageCollectionProcessing(
615
    GarbageCollector collector) {
616 617 618
  // Process weak global handle callbacks. This must be done after the
  // GC is completely done, because the callbacks may invoke arbitrary
  // API functions.
619
  DCHECK(isolate_->heap()->gc_state() == Heap::NOT_IN_GC);
620
  const int initial_post_gc_processing_count = ++post_gc_processing_count_;
621
  int freed_nodes = 0;
622 623 624
  if (collector == SCAVENGER) {
    for (int i = 0; i < new_space_nodes_.length(); ++i) {
      Node* node = new_space_nodes_[i];
625
      DCHECK(node->is_in_new_space_list());
626 627
      if (!node->IsRetainer()) {
        // Free nodes do not have weak callbacks. Do not use them to compute
628
        // the freed_nodes.
629 630
        continue;
      }
631 632 633
      // Skip dependent handles. Their weak callbacks might expect to be
      // called between two global garbage collection callbacks which
      // are not called for minor collections.
634 635 636 637
      if (!node->is_independent() && !node->is_partially_dependent()) {
        continue;
      }
      node->clear_partially_dependent();
638
      if (node->PostGarbageCollectionProcessing(isolate_)) {
639 640 641 642 643
        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).
644
          return freed_nodes;
645 646 647
        }
      }
      if (!node->IsRetainer()) {
648
        freed_nodes++;
649 650
      }
    }
651
  } else {
652 653 654
    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
655
        // the freed_nodes.
656
        continue;
657
      }
658 659 660 661
      it.node()->clear_partially_dependent();
      if (it.node()->PostGarbageCollectionProcessing(isolate_)) {
        if (initial_post_gc_processing_count != post_gc_processing_count_) {
          // See the comment above.
662
          return freed_nodes;
663
        }
664
      }
665
      if (!it.node()->IsRetainer()) {
666
        freed_nodes++;
667
      }
668 669
    }
  }
670 671 672 673
  // Update the list of new space nodes.
  int last = 0;
  for (int i = 0; i < new_space_nodes_.length(); ++i) {
    Node* node = new_space_nodes_[i];
674
    DCHECK(node->is_in_new_space_list());
675 676 677
    if (node->IsRetainer()) {
      if (isolate_->heap()->InNewSpace(node->object())) {
        new_space_nodes_[last++] = node;
678
        isolate_->heap()->IncrementNodesCopiedInNewSpace();
679 680
      } else {
        node->set_in_new_space_list(false);
681
        isolate_->heap()->IncrementNodesPromoted();
682
      }
683 684
    } else {
      node->set_in_new_space_list(false);
685
      isolate_->heap()->IncrementNodesDiedInNewSpace();
686
    }
687
  }
688
  new_space_nodes_.Rewind(last);
689
  return freed_nodes;
690 691 692
}


693
void GlobalHandles::IterateStrongRoots(ObjectVisitor* v) {
694 695 696
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsStrongRetainer()) {
      v->VisitPointer(it.node()->location());
697 698 699 700
    }
  }
}

701

702
void GlobalHandles::IterateAllRoots(ObjectVisitor* v) {
703 704 705
  for (NodeIterator it(this); !it.done(); it.Advance()) {
    if (it.node()->IsRetainer()) {
      v->VisitPointer(it.node()->location());
706 707 708 709 710
    }
  }
}


711
void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
712
  for (NodeIterator it(this); !it.done(); it.Advance()) {
713
    if (it.node()->IsRetainer() && it.node()->has_wrapper_class_id()) {
714 715
      v->VisitEmbedderReference(it.node()->location(),
                                it.node()->wrapper_class_id());
716 717 718 719 720
    }
  }
}


721 722 723 724 725 726 727 728 729 730 731
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());
    }
  }
}


732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
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;
}


755
void GlobalHandles::RecordStats(HeapStats* stats) {
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
  *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;
    }
  }
773 774
}

775 776 777
#ifdef DEBUG

void GlobalHandles::PrintStats() {
778 779 780 781 782 783 784 785 786 787 788 789 790 791
  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++;
  }

792
  PrintF("Global Handle Statistics:\n");
793 794 795 796 797
  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);
798 799 800
  PrintF("  # total      = %d\n", total);
}

801

802 803
void GlobalHandles::Print() {
  PrintF("Global handles:\n");
804 805 806 807 808
  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)" : "");
809 810 811 812 813 814
  }
}

#endif


815 816 817 818

void GlobalHandles::AddObjectGroup(Object*** handles,
                                   size_t length,
                                   v8::RetainedObjectInfo* info) {
819 820
#ifdef DEBUG
  for (size_t i = 0; i < length; ++i) {
821
    DCHECK(!Node::FromLocation(handles[i])->is_independent());
822 823
  }
#endif
824 825 826
  if (length == 0) {
    if (info != NULL) info->Dispose();
    return;
827
  }
828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
  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));
845 846
}

847

848
void GlobalHandles::SetReferenceFromGroup(UniqueId id, Object** child) {
849
  DCHECK(!Node::FromLocation(child)->is_independent());
850 851 852 853 854
  implicit_ref_connections_.Add(ObjectGroupConnection(id, child));
}


void GlobalHandles::SetReference(HeapObject** parent, Object** child) {
855
  DCHECK(!Node::FromLocation(child)->is_independent());
856 857 858
  ImplicitRefGroup* group = new ImplicitRefGroup(parent, 1);
  group->children[0] = child;
  implicit_ref_groups_.Add(group);
859 860 861
}


862
void GlobalHandles::RemoveObjectGroups() {
863 864
  for (int i = 0; i < object_groups_.length(); i++)
    delete object_groups_.at(i);
865
  object_groups_.Clear();
866 867 868 869 870
  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);
871 872
}

873 874

void GlobalHandles::RemoveImplicitRefGroups() {
875
  for (int i = 0; i < implicit_ref_groups_.length(); i++) {
876
    delete implicit_ref_groups_.at(i);
877
  }
878
  implicit_ref_groups_.Clear();
879
  implicit_ref_connections_.Clear();
880 881 882
}


883 884 885 886 887
void GlobalHandles::TearDown() {
  // TODO(1428): invoke weak callbacks.
}


888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
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();
}


992
EternalHandles::EternalHandles() : size_(0) {
993
  for (unsigned i = 0; i < arraysize(singleton_handles_); i++) {
994 995 996 997 998 999
    singleton_handles_[i] = kInvalidIndex;
  }
}


EternalHandles::~EternalHandles() {
1000
  for (int i = 0; i < blocks_.length(); i++) delete[] blocks_[i];
1001 1002 1003 1004 1005 1006
}


void EternalHandles::IterateAllRoots(ObjectVisitor* visitor) {
  int limit = size_;
  for (int i = 0; i < blocks_.length(); i++) {
1007
    DCHECK(limit > 0);
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
    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);
}


1034
void EternalHandles::Create(Isolate* isolate, Object* object, int* index) {
1035
  DCHECK_EQ(kInvalidIndex, *index);
1036
  if (object == NULL) return;
1037
  DCHECK_NE(isolate->heap()->the_hole_value(), object);
1038 1039 1040 1041 1042 1043 1044 1045 1046
  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);
  }
1047
  DCHECK_EQ(isolate->heap()->the_hole_value(), blocks_[block][offset]);
1048 1049 1050 1051
  blocks_[block][offset] = object;
  if (isolate->heap()->InNewSpace(object)) {
    new_space_indices_.Add(size_);
  }
1052
  *index = size_++;
1053 1054 1055
}


1056
} }  // namespace v8::internal