diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index ed513b5b80d74a..649deb515aa395 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 6 #define V8_MINOR_VERSION 7 #define V8_BUILD_NUMBER 288 -#define V8_PATCH_LEVEL 44 +#define V8_PATCH_LEVEL 45 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 6fb5e50dbeed90..ab77238a77f97f 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -9071,6 +9071,7 @@ class Internals { static const int kNodeStateIsWeakValue = 2; static const int kNodeStateIsPendingValue = 3; static const int kNodeStateIsNearDeathValue = 4; + static const int kNodeIsIndependentShift = 3; static const int kNodeIsActiveShift = 4; static const int kFirstNonstringType = 0x80; @@ -9294,7 +9295,10 @@ void Persistent<T, M>::Copy(const Persistent<S, M2>& that) { template <class T> bool PersistentBase<T>::IsIndependent() const { - return true; + typedef internal::Internals I; + if (this->IsEmpty()) return false; + return I::GetNodeFlag(reinterpret_cast<internal::Object**>(this->val_), + I::kNodeIsIndependentShift); } template <class T> @@ -9383,7 +9387,12 @@ void PersistentBase<T>::RegisterExternalReference(Isolate* isolate) const { } template <class T> -void PersistentBase<T>::MarkIndependent() {} +void PersistentBase<T>::MarkIndependent() { + typedef internal::Internals I; + if (this->IsEmpty()) return; + I::UpdateNodeFlag(reinterpret_cast<internal::Object**>(this->val_), true, + I::kNodeIsIndependentShift); +} template <class T> void PersistentBase<T>::MarkActive() { diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 90467168ab67e1..0e9b678ceb7eb8 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -41,6 +41,8 @@ class GlobalHandles::Node { STATIC_ASSERT(WEAK == Internals::kNodeStateIsWeakValue); STATIC_ASSERT(PENDING == Internals::kNodeStateIsPendingValue); STATIC_ASSERT(NEAR_DEATH == Internals::kNodeStateIsNearDeathValue); + STATIC_ASSERT(static_cast<int>(IsIndependent::kShift) == + Internals::kNodeIsIndependentShift); STATIC_ASSERT(static_cast<int>(IsActive::kShift) == Internals::kNodeIsActiveShift); } @@ -52,6 +54,7 @@ class GlobalHandles::Node { object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; index_ = 0; + set_independent(false); set_active(false); set_in_new_space_list(false); data_.next_free = nullptr; @@ -73,6 +76,7 @@ class GlobalHandles::Node { DCHECK(state() == FREE); object_ = object; class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; + set_independent(false); set_active(false); set_state(NORMAL); data_.parameter = nullptr; @@ -92,6 +96,7 @@ class GlobalHandles::Node { // Zap the values for eager trapping. object_ = reinterpret_cast<Object*>(kGlobalHandleZapValue); class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId; + set_independent(false); set_active(false); weak_callback_ = nullptr; DecreaseBlockUses(); @@ -119,6 +124,9 @@ class GlobalHandles::Node { flags_ = NodeState::update(flags_, state); } + bool is_independent() { return IsIndependent::decode(flags_); } + void set_independent(bool v) { flags_ = IsIndependent::update(flags_, v); } + bool is_active() { return IsActive::decode(flags_); } @@ -183,6 +191,12 @@ class GlobalHandles::Node { set_state(PENDING); } + // Independent flag accessors. + void MarkIndependent() { + DCHECK(IsInUse()); + set_independent(true); + } + // Callback parameter accessors. void set_parameter(void* parameter) { DCHECK(IsInUse()); @@ -330,7 +344,7 @@ class GlobalHandles::Node { // Placed first to avoid offset computation. Object* object_; - // Next word stores class_id, index, and state. + // Next word stores class_id, index, state, and independent. // Note: the most aligned fields should go first. // Wrapper class ID. @@ -339,7 +353,10 @@ class GlobalHandles::Node { // Index in the containing handle block. uint8_t index_; + // This stores three flags (independent, partially_dependent and + // in_new_space_list) and a State. class NodeState : public BitField<State, 0, 3> {}; + class IsIndependent : public BitField<bool, 3, 1> {}; // The following two fields are mutually exclusive class IsActive : public BitField<bool, 4, 1> {}; class IsInNewSpaceList : public BitField<bool, 5, 1> {}; @@ -591,6 +608,14 @@ void GlobalHandles::AnnotateStrongRetainer(Object** location, Node::FromLocation(location)->AnnotateStrongRetainer(label); } +void GlobalHandles::MarkIndependent(Object** location) { + Node::FromLocation(location)->MarkIndependent(); +} + +bool GlobalHandles::IsIndependent(Object** location) { + return Node::FromLocation(location)->is_independent(); +} + bool GlobalHandles::IsNearDeath(Object** location) { return Node::FromLocation(location)->IsNearDeath(); } @@ -647,7 +672,8 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback should_reset_handle) { void GlobalHandles::IterateNewSpaceStrongAndDependentRoots(RootVisitor* v) { for (Node* node : new_space_nodes_) { if (node->IsStrongRetainer() || - (node->IsWeakRetainer() && node->is_active())) { + (node->IsWeakRetainer() && !node->is_independent() && + node->is_active())) { v->VisitRootPointer(Root::kGlobalHandles, node->label(), node->location()); } @@ -662,7 +688,8 @@ void GlobalHandles::IterateNewSpaceStrongAndDependentRootsAndIdentifyUnmodified( node->set_active(true); } if (node->IsStrongRetainer() || - (node->IsWeakRetainer() && node->is_active())) { + (node->IsWeakRetainer() && !node->is_independent() && + node->is_active())) { v->VisitRootPointer(Root::kGlobalHandles, node->label(), node->location()); } @@ -682,8 +709,8 @@ void GlobalHandles::MarkNewSpaceWeakUnmodifiedObjectsPending( WeakSlotCallbackWithHeap is_dead) { for (Node* node : new_space_nodes_) { DCHECK(node->is_in_new_space_list()); - if (node->IsWeak() && is_dead(isolate_->heap(), node->location())) { - DCHECK(!node->is_active()); + if ((node->is_independent() || !node->is_active()) && node->IsWeak() && + is_dead(isolate_->heap(), node->location())) { if (!node->IsPhantomCallback() && !node->IsPhantomResetHandle()) { node->MarkPending(); } @@ -695,8 +722,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForFinalizers( RootVisitor* v) { for (Node* node : new_space_nodes_) { DCHECK(node->is_in_new_space_list()); - if (!node->is_active() && node->IsWeakRetainer() && - (node->state() == Node::PENDING)) { + if ((node->is_independent() || !node->is_active()) && + node->IsWeakRetainer() && (node->state() == Node::PENDING)) { DCHECK(!node->IsPhantomCallback()); DCHECK(!node->IsPhantomResetHandle()); // Finalizers need to survive. @@ -710,8 +737,8 @@ void GlobalHandles::IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles( RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle) { for (Node* node : new_space_nodes_) { DCHECK(node->is_in_new_space_list()); - if (!node->is_active() && node->IsWeakRetainer() && - (node->state() != Node::PENDING)) { + if ((node->is_independent() || !node->is_active()) && + node->IsWeakRetainer() && (node->state() != Node::PENDING)) { DCHECK(node->IsPhantomResetHandle() || node->IsPhantomCallback()); if (should_reset_handle(isolate_->heap(), node->location())) { if (node->IsPhantomResetHandle()) { @@ -757,12 +784,15 @@ int GlobalHandles::PostScavengeProcessing( // the freed_nodes. continue; } - - // Active nodes are kept alive, so no further processing is requires. - if (node->is_active()) { + // Skip dependent or unmodified handles. Their weak callbacks might expect + // to be + // called between two global garbage collection callbacks which + // are not called for minor collections. + if (!node->is_independent() && (node->is_active())) { node->set_active(false); continue; } + node->set_active(false); if (node->PostGarbageCollectionProcessing(isolate_)) { if (initial_post_gc_processing_count != post_gc_processing_count_) { @@ -773,7 +803,6 @@ int GlobalHandles::PostScavengeProcessing( return freed_nodes; } } - if (!node->IsRetainer()) { freed_nodes++; } diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h index e96b74b8834622..2c2fbbd3f95e97 100644 --- a/deps/v8/src/global-handles.h +++ b/deps/v8/src/global-handles.h @@ -99,6 +99,11 @@ class GlobalHandles { // Clear the weakness of a global handle. static void* ClearWeakness(Object** location); + // Mark the reference to this object independent. + static void MarkIndependent(Object** location); + + static bool IsIndependent(Object** location); + // Tells whether global handle is near death. static bool IsNearDeath(Object** location); @@ -155,7 +160,8 @@ class GlobalHandles { void MarkNewSpaceWeakUnmodifiedObjectsPending( WeakSlotCallbackWithHeap is_dead); - // Iterates over weak unmodified handles. See the note above. + // Iterates over weak independent or unmodified handles. + // See the note above. void IterateNewSpaceWeakUnmodifiedRootsForFinalizers(RootVisitor* v); void IterateNewSpaceWeakUnmodifiedRootsForPhantomHandles( RootVisitor* v, WeakSlotCallbackWithHeap should_reset_handle); diff --git a/deps/v8/src/profiler/sampling-heap-profiler.cc b/deps/v8/src/profiler/sampling-heap-profiler.cc index 6912f3eba11d42..734c2ea36d7273 100644 --- a/deps/v8/src/profiler/sampling-heap-profiler.cc +++ b/deps/v8/src/profiler/sampling-heap-profiler.cc @@ -99,6 +99,7 @@ void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { Sample* sample = new Sample(size, node, loc, this); samples_.emplace(sample); sample->global.SetWeak(sample, OnWeakCallback, WeakCallbackType::kParameter); + sample->global.MarkIndependent(); } void SamplingHeapProfiler::OnWeakCallback( diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 4ebd93b86c5759..d698c1a9e008a4 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -7807,6 +7807,80 @@ struct FlagAndPersistent { v8::Global<v8::Object> handle; }; +static void SetFlag(const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + data.GetParameter()->flag = true; + data.GetParameter()->handle.Reset(); +} + +static void IndependentWeakHandle(bool global_gc, bool interlinked) { + i::FLAG_stress_incremental_marking = false; + // Parallel scavenge introduces too much fragmentation. + i::FLAG_parallel_scavenge = false; + v8::Isolate* iso = CcTest::isolate(); + v8::HandleScope scope(iso); + v8::Local<Context> context = Context::New(iso); + Context::Scope context_scope(context); + + FlagAndPersistent object_a, object_b; + + size_t big_heap_size = 0; + size_t big_array_size = 0; + + { + v8::HandleScope handle_scope(iso); + Local<Object> a(v8::Object::New(iso)); + Local<Object> b(v8::Object::New(iso)); + object_a.handle.Reset(iso, a); + object_b.handle.Reset(iso, b); + if (interlinked) { + a->Set(context, v8_str("x"), b).FromJust(); + b->Set(context, v8_str("x"), a).FromJust(); + } + if (global_gc) { + CcTest::CollectAllGarbage(); + } else { + CcTest::CollectGarbage(i::NEW_SPACE); + } + v8::Local<Value> big_array = v8::Array::New(CcTest::isolate(), 5000); + // Verify that we created an array where the space was reserved up front. + big_array_size = + v8::internal::JSArray::cast(*v8::Utils::OpenHandle(*big_array)) + ->elements() + ->Size(); + CHECK_LE(20000, big_array_size); + a->Set(context, v8_str("y"), big_array).FromJust(); + big_heap_size = CcTest::heap()->SizeOfObjects(); + } + + object_a.flag = false; + object_b.flag = false; + object_a.handle.SetWeak(&object_a, &SetFlag, + v8::WeakCallbackType::kParameter); + object_b.handle.SetWeak(&object_b, &SetFlag, + v8::WeakCallbackType::kParameter); + CHECK(!object_b.handle.IsIndependent()); + object_a.handle.MarkIndependent(); + object_b.handle.MarkIndependent(); + CHECK(object_b.handle.IsIndependent()); + if (global_gc) { + CcTest::CollectAllGarbage(); + } else { + CcTest::CollectGarbage(i::NEW_SPACE); + } + // A single GC should be enough to reclaim the memory, since we are using + // phantom handles. + CHECK_GT(big_heap_size - big_array_size, CcTest::heap()->SizeOfObjects()); + CHECK(object_a.flag); + CHECK(object_b.flag); +} + +TEST(IndependentWeakHandle) { + IndependentWeakHandle(false, false); + IndependentWeakHandle(false, true); + IndependentWeakHandle(true, false); + IndependentWeakHandle(true, true); +} + class Trivial { public: explicit Trivial(int x) : x_(x) {} @@ -7899,6 +7973,125 @@ THREADED_TEST(InternalFieldCallback) { InternalFieldCallback(true); } +static void ResetUseValueAndSetFlag( + const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + // Blink will reset the handle, and then use the other handle, so they + // can't use the same backing slot. + data.GetParameter()->handle.Reset(); + data.GetParameter()->flag = true; +} + +void v8::internal::heap::HeapTester::ResetWeakHandle(bool global_gc) { + using v8::Context; + using v8::Local; + using v8::Object; + + v8::Isolate* iso = CcTest::isolate(); + v8::HandleScope scope(iso); + v8::Local<Context> context = Context::New(iso); + Context::Scope context_scope(context); + + FlagAndPersistent object_a, object_b; + + { + v8::HandleScope handle_scope(iso); + Local<Object> a(v8::Object::New(iso)); + Local<Object> b(v8::Object::New(iso)); + object_a.handle.Reset(iso, a); + object_b.handle.Reset(iso, b); + if (global_gc) { + CcTest::CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + } else { + CcTest::CollectGarbage(i::NEW_SPACE); + } + } + + object_a.flag = false; + object_b.flag = false; + object_a.handle.SetWeak(&object_a, &ResetUseValueAndSetFlag, + v8::WeakCallbackType::kParameter); + object_b.handle.SetWeak(&object_b, &ResetUseValueAndSetFlag, + v8::WeakCallbackType::kParameter); + if (!global_gc) { + object_a.handle.MarkIndependent(); + object_b.handle.MarkIndependent(); + CHECK(object_b.handle.IsIndependent()); + } + if (global_gc) { + CcTest::CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + } else { + CcTest::CollectGarbage(i::NEW_SPACE); + } + CHECK(object_a.flag); + CHECK(object_b.flag); +} + +THREADED_HEAP_TEST(ResetWeakHandle) { + v8::internal::heap::HeapTester::ResetWeakHandle(false); + v8::internal::heap::HeapTester::ResetWeakHandle(true); +} + +static void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); } + +static void InvokeMarkSweep() { CcTest::CollectAllGarbage(); } + +static void ForceScavenge2( + const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + data.GetParameter()->flag = true; + InvokeScavenge(); +} + +static void ForceScavenge1( + const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + data.GetParameter()->handle.Reset(); + data.SetSecondPassCallback(ForceScavenge2); +} + +static void ForceMarkSweep2( + const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + data.GetParameter()->flag = true; + InvokeMarkSweep(); +} + +static void ForceMarkSweep1( + const v8::WeakCallbackInfo<FlagAndPersistent>& data) { + data.GetParameter()->handle.Reset(); + data.SetSecondPassCallback(ForceMarkSweep2); +} + +THREADED_TEST(GCFromWeakCallbacks) { + v8::Isolate* isolate = CcTest::isolate(); + v8::Locker locker(CcTest::isolate()); + v8::HandleScope scope(isolate); + v8::Local<Context> context = Context::New(isolate); + Context::Scope context_scope(context); + + static const int kNumberOfGCTypes = 2; + typedef v8::WeakCallbackInfo<FlagAndPersistent>::Callback Callback; + Callback gc_forcing_callback[kNumberOfGCTypes] = {&ForceScavenge1, + &ForceMarkSweep1}; + + typedef void (*GCInvoker)(); + GCInvoker invoke_gc[kNumberOfGCTypes] = {&InvokeScavenge, &InvokeMarkSweep}; + + for (int outer_gc = 0; outer_gc < kNumberOfGCTypes; outer_gc++) { + for (int inner_gc = 0; inner_gc < kNumberOfGCTypes; inner_gc++) { + FlagAndPersistent object; + { + v8::HandleScope handle_scope(isolate); + object.handle.Reset(isolate, v8::Object::New(isolate)); + } + object.flag = false; + object.handle.SetWeak(&object, gc_forcing_callback[inner_gc], + v8::WeakCallbackType::kParameter); + object.handle.MarkIndependent(); + invoke_gc[outer_gc](); + EmptyMessageQueues(isolate); + CHECK(object.flag); + } + } +} + v8::Local<Function> args_fun;