summaryrefslogtreecommitdiff
path: root/deps/v8/test/cctest/test-global-handles.cc
diff options
context:
space:
mode:
authorMichaƫl Zasso <targos@protonmail.com>2018-01-24 20:16:06 +0100
committerMyles Borins <mylesborins@google.com>2018-01-24 15:02:20 -0800
commit4c4af643e5042d615a60c6bbc05aee9d81b903e5 (patch)
tree3fb0a97988fe4439ae3ae06f26915d1dcf8cab92 /deps/v8/test/cctest/test-global-handles.cc
parentfa9f31a4fda5a3782c652e56e394465805ebb50f (diff)
downloadnode-new-4c4af643e5042d615a60c6bbc05aee9d81b903e5.tar.gz
deps: update V8 to 6.4.388.40
PR-URL: https://github.com/nodejs/node/pull/17489 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Diffstat (limited to 'deps/v8/test/cctest/test-global-handles.cc')
-rw-r--r--deps/v8/test/cctest/test-global-handles.cc338
1 files changed, 300 insertions, 38 deletions
diff --git a/deps/v8/test/cctest/test-global-handles.cc b/deps/v8/test/cctest/test-global-handles.cc
index d3e229530f..f4a3072763 100644
--- a/deps/v8/test/cctest/test-global-handles.cc
+++ b/deps/v8/test/cctest/test-global-handles.cc
@@ -36,6 +36,102 @@
namespace v8 {
namespace internal {
+namespace {
+
+void SimpleCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ info.GetReturnValue().Set(v8_num(0));
+}
+
+struct FlagAndPersistent {
+ bool flag;
+ v8::Global<v8::Object> handle;
+};
+
+void ResetHandleAndSetFlag(
+ const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
+ data.GetParameter()->handle.Reset();
+ data.GetParameter()->flag = true;
+}
+
+using ConstructFunction = void (*)(v8::Isolate* isolate,
+ v8::Local<v8::Context> context,
+ FlagAndPersistent* flag_and_persistent);
+
+void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
+ FlagAndPersistent* flag_and_persistent) {
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::Object> object(v8::Object::New(isolate));
+ CHECK(!object.IsEmpty());
+ flag_and_persistent->handle.Reset(isolate, object);
+ CHECK(!flag_and_persistent->handle.IsEmpty());
+}
+
+void ConstructJSApiObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
+ FlagAndPersistent* flag_and_persistent) {
+ v8::HandleScope handle_scope(isolate);
+ v8::Local<v8::FunctionTemplate> fun =
+ v8::FunctionTemplate::New(isolate, SimpleCallback);
+ v8::Local<v8::Object> object = fun->GetFunction(context)
+ .ToLocalChecked()
+ ->NewInstance(context)
+ .ToLocalChecked();
+ CHECK(!object.IsEmpty());
+ flag_and_persistent->handle.Reset(isolate, object);
+ CHECK(!flag_and_persistent->handle.IsEmpty());
+}
+
+enum class SurvivalMode { kSurvives, kDies };
+
+template <typename ModifierFunction, typename GCFunction>
+void WeakHandleTest(v8::Isolate* isolate, ConstructFunction construct_function,
+ ModifierFunction modifier_function, GCFunction gc_function,
+ SurvivalMode survives) {
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::Context::Scope context_scope(context);
+
+ FlagAndPersistent fp;
+ construct_function(isolate, context, &fp);
+ {
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Object> tmp = v8::Local<v8::Object>::New(isolate, fp.handle);
+ CHECK(
+ CcTest::i_isolate()->heap()->InNewSpace(*v8::Utils::OpenHandle(*tmp)));
+ }
+
+ fp.handle.SetWeak(&fp, &ResetHandleAndSetFlag,
+ v8::WeakCallbackType::kParameter);
+ fp.flag = false;
+ modifier_function(&fp);
+ gc_function();
+ CHECK_IMPLIES(survives == SurvivalMode::kSurvives, !fp.flag);
+ CHECK_IMPLIES(survives == SurvivalMode::kDies, fp.flag);
+}
+
+void ResurrectingFinalizer(
+ const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
+ data.GetParameter()->ClearWeak();
+}
+
+void ResettingFinalizer(
+ const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
+ data.GetParameter()->Reset();
+}
+
+void EmptyWeakCallback(const v8::WeakCallbackInfo<void>& data) {}
+
+void ResurrectingFinalizerSettingProperty(
+ const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
+ data.GetParameter()->ClearWeak();
+ v8::Local<v8::Object> o =
+ v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
+ o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
+ v8_str("was here"))
+ .FromJust();
+}
+
+} // namespace
+
TEST(EternalHandles) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
@@ -117,10 +213,6 @@ TEST(PersistentBaseGetLocal) {
CHECK(v8::Local<v8::Object>::New(isolate, g) == g.Get(isolate));
}
-
-void WeakCallback(const v8::WeakCallbackInfo<void>& data) {}
-
-
TEST(WeakPersistentSmi) {
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
@@ -130,16 +222,8 @@ TEST(WeakPersistentSmi) {
v8::Global<v8::Number> g(isolate, n);
// Should not crash.
- g.SetWeak<void>(nullptr, &WeakCallback, v8::WeakCallbackType::kParameter);
-}
-
-void finalizer(const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
- data.GetParameter()->ClearWeak();
- v8::Local<v8::Object> o =
- v8::Local<v8::Object>::New(data.GetIsolate(), *data.GetParameter());
- o->Set(data.GetIsolate()->GetCurrentContext(), v8_str("finalizer"),
- v8_str("was here"))
- .FromJust();
+ g.SetWeak<void>(nullptr, &EmptyWeakCallback,
+ v8::WeakCallbackType::kParameter);
}
TEST(FinalizerWeakness) {
@@ -154,7 +238,8 @@ TEST(FinalizerWeakness) {
v8::Local<v8::Object> o = v8::Object::New(isolate);
identity = o->GetIdentityHash();
g.Reset(isolate, o);
- g.SetWeak(&g, finalizer, v8::WeakCallbackType::kFinalizer);
+ g.SetWeak(&g, &ResurrectingFinalizerSettingProperty,
+ v8::WeakCallbackType::kFinalizer);
}
CcTest::CollectAllAvailableGarbage();
@@ -185,41 +270,218 @@ TEST(PhatomHandlesWithoutCallbacks) {
CHECK_EQ(0u, isolate->NumberOfPhantomHandleResetsSinceLastCall());
}
+TEST(WeakHandleToUnmodifiedJSObjectSurvivesScavenge) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
+ []() { CcTest::CollectGarbage(i::NEW_SPACE); }, SurvivalMode::kSurvives);
+}
+
+TEST(WeakHandleToUnmodifiedJSObjectDiesOnMarkCompact) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSObject, [](FlagAndPersistent* fp) {},
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); }, SurvivalMode::kDies);
+}
+
+TEST(WeakHandleToUnmodifiedJSObjectSurvivesMarkCompactWhenInHandle) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSObject,
+ [](FlagAndPersistent* fp) {
+ v8::Local<v8::Object> handle =
+ v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
+ USE(handle);
+ },
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); }, SurvivalMode::kSurvives);
+}
+
+TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnScavenge) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
+ []() { CcTest::CollectGarbage(i::NEW_SPACE); }, SurvivalMode::kDies);
+}
+
+TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesScavengeWhenInHandle) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSApiObject,
+ [](FlagAndPersistent* fp) {
+ v8::Local<v8::Object> handle =
+ v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
+ USE(handle);
+ },
+ []() { CcTest::CollectGarbage(i::NEW_SPACE); }, SurvivalMode::kSurvives);
+}
+
+TEST(WeakHandleToUnmodifiedJSApiObjectDiesOnMarkCompact) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSApiObject, [](FlagAndPersistent* fp) {},
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); }, SurvivalMode::kDies);
+}
+
+TEST(WeakHandleToUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSApiObject,
+ [](FlagAndPersistent* fp) {
+ v8::Local<v8::Object> handle =
+ v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
+ USE(handle);
+ },
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); }, SurvivalMode::kSurvives);
+}
+
+TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesScavenge) {
+ CcTest::InitializeVM();
+ WeakHandleTest(CcTest::isolate(), &ConstructJSApiObject,
+ [](FlagAndPersistent* fp) { fp->handle.MarkActive(); },
+ []() { CcTest::CollectGarbage(i::NEW_SPACE); },
+ SurvivalMode::kSurvives);
+}
+
+TEST(WeakHandleToActiveUnmodifiedJSApiObjectDiesOnMarkCompact) {
+ CcTest::InitializeVM();
+ WeakHandleTest(CcTest::isolate(), &ConstructJSApiObject,
+ [](FlagAndPersistent* fp) { fp->handle.MarkActive(); },
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); },
+ SurvivalMode::kDies);
+}
+
+TEST(WeakHandleToActiveUnmodifiedJSApiObjectSurvivesMarkCompactWhenInHandle) {
+ CcTest::InitializeVM();
+ WeakHandleTest(
+ CcTest::isolate(), &ConstructJSApiObject,
+ [](FlagAndPersistent* fp) {
+ fp->handle.MarkActive();
+ v8::Local<v8::Object> handle =
+ v8::Local<v8::Object>::New(CcTest::isolate(), fp->handle);
+ USE(handle);
+ },
+ []() { CcTest::CollectGarbage(i::OLD_SPACE); }, SurvivalMode::kSurvives);
+}
+
namespace {
-void ResurrectingFinalizer(
- const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
- data.GetParameter()->ClearWeak();
+void ConstructFinalizerPointingPhantomHandle(
+ v8::Isolate* isolate, v8::Global<v8::Object>* g1,
+ v8::Global<v8::Object>* g2,
+ typename v8::WeakCallbackInfo<v8::Global<v8::Object>>::Callback
+ finalizer_for_g1) {
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Object> o1 =
+ v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
+ v8::Local<v8::Object> o2 =
+ v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
+ o1->Set(isolate->GetCurrentContext(), v8_str("link"), o2).FromJust();
+ g1->Reset(isolate, o1);
+ g2->Reset(isolate, o2);
+ // g1 will be finalized but resurrected.
+ g1->SetWeak(g1, finalizer_for_g1, v8::WeakCallbackType::kFinalizer);
+ // g2 will be a phantom handle that is dependent on the finalizer handle
+ // g1 as it is in its subgraph.
+ g2->SetWeak();
}
} // namespace
-TEST(Regress772299) {
+TEST(FinalizerResurrectsAndKeepsPhantomAliveOnMarkCompact) {
+ // See crbug.com/772299.
CcTest::InitializeVM();
- v8::Isolate* isolate = CcTest::isolate();
-
v8::Global<v8::Object> g1, g2;
- {
- v8::HandleScope scope(isolate);
- v8::Local<v8::Object> o1 =
- v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
- v8::Local<v8::Object> o2 =
- v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
- o1->Set(isolate->GetCurrentContext(), v8_str("link"), o2).FromJust();
- g1.Reset(isolate, o1);
- g2.Reset(isolate, o2);
- // g1 will be finalized but resurrected.
- g1.SetWeak(&g1, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer);
- // g2 will be a phantom handle that should not be reset as g1 transitively
- // keeps it alive.
- g2.SetWeak();
- }
-
- CcTest::CollectAllAvailableGarbage();
+ ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
+ ResurrectingFinalizer);
+ CcTest::CollectGarbage(i::OLD_SPACE);
// Both, g1 and g2, should stay alive as the finalizer resurrects the root
// object that transitively keeps the other one alive.
CHECK(!g1.IsEmpty());
CHECK(!g2.IsEmpty());
+ CcTest::CollectGarbage(i::OLD_SPACE);
+ // The finalizer handle is now strong, so it should keep the objects alive.
+ CHECK(!g1.IsEmpty());
+ CHECK(!g2.IsEmpty());
+}
+
+TEST(FinalizerDiesAndKeepsPhantomAliveOnMarkCompact) {
+ CcTest::InitializeVM();
+ v8::Global<v8::Object> g1, g2;
+ ConstructFinalizerPointingPhantomHandle(CcTest::isolate(), &g1, &g2,
+ ResettingFinalizer);
+ CcTest::CollectGarbage(i::OLD_SPACE);
+ // Finalizer (g1) dies but the phantom handle (g2) is kept alive for one
+ // more round as the underlying object only dies on the next GC.
+ CHECK(g1.IsEmpty());
+ CHECK(!g2.IsEmpty());
+ CcTest::CollectGarbage(i::OLD_SPACE);
+ // Phantom handle dies after one more round.
+ CHECK(g1.IsEmpty());
+ CHECK(g2.IsEmpty());
+}
+
+namespace {
+
+void InvokeScavenge() { CcTest::CollectGarbage(i::NEW_SPACE); }
+
+void InvokeMarkSweep() { CcTest::CollectAllGarbage(); }
+
+void ForceScavenge2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
+ data.GetParameter()->flag = true;
+ InvokeScavenge();
+}
+
+void ForceScavenge1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
+ data.GetParameter()->handle.Reset();
+ data.SetSecondPassCallback(ForceScavenge2);
+}
+
+void ForceMarkSweep2(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
+ data.GetParameter()->flag = true;
+ InvokeMarkSweep();
+}
+
+void ForceMarkSweep1(const v8::WeakCallbackInfo<FlagAndPersistent>& data) {
+ data.GetParameter()->handle.Reset();
+ data.SetSecondPassCallback(ForceMarkSweep2);
+}
+
+} // namespace
+
+TEST(GCFromWeakCallbacks) {
+ v8::Isolate* isolate = CcTest::isolate();
+ v8::Locker locker(CcTest::isolate());
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Context> context = v8::Context::New(isolate);
+ v8::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 fp;
+ ConstructJSApiObject(isolate, context, &fp);
+ {
+ v8::HandleScope scope(isolate);
+ v8::Local<v8::Object> tmp =
+ v8::Local<v8::Object>::New(isolate, fp.handle);
+ CHECK(CcTest::i_isolate()->heap()->InNewSpace(
+ *v8::Utils::OpenHandle(*tmp)));
+ }
+ fp.flag = false;
+ fp.handle.SetWeak(&fp, gc_forcing_callback[inner_gc],
+ v8::WeakCallbackType::kParameter);
+ invoke_gc[outer_gc]();
+ EmptyMessageQueues(isolate);
+ CHECK(fp.flag);
+ }
+ }
}
} // namespace internal