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