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