Skip to content

Commit da728c4

Browse files
addaleaxBethGriggs
authored andcommitted
deps: V8: cherry-pick 9f0f2cb7f08d
Original commit message: [weakrefs] Call Isolate::ClearKeptObjects() as part of microtask checkpoint In the spec, WeakRefs that are dereferenced are kept alive until there's no JS on the stack, and then the host is expected to call ClearKeptObjects to clear those strong references [1]. HTML calls ClearKeptObjects at the end of a PerformMicrotaskCheckpoint [2]. In V8, leaving this up to the embedder is error prone in the same way the deprecated FinalizationGroup callback APIs were error prone: it depends on the embedder doing the right thing. This CL moves the call to ClearKeptObjects to be after running of microtasks within V8. However, the Isolate::ClearKeptObjects API should not be removed or deprecated in case an embedder uses an entirely custom MicrotaskQueue implementation and invokes MicrotaskQueue::PerformCheckpoint manually. [1] https://tc39.es/proposal-weakrefs/#sec-clear-kept-objects [2] whatwg/html#4571 Bug: v8:8179 Change-Id: Ie243804157b56241ca69ed8fad300e839a0c9f75 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2055967 Commit-Queue: Shu-yu Guo <[email protected]> Reviewed-by: Ulan Degenbaev <[email protected]> Reviewed-by: Ross McIlroy <[email protected]> Cr-Commit-Position: refs/heads/master@{#66327} Refs: v8/v8@9f0f2cb PR-URL: #32885 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Ujjwal Sharma <[email protected]> Reviewed-By: Gerhard Stöbich <[email protected]> Reviewed-By: Beth Griggs <[email protected]>
1 parent 2ee8b4a commit da728c4

File tree

11 files changed

+93
-65
lines changed

11 files changed

+93
-65
lines changed

common.gypi

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
# Reset this number to 0 on major V8 upgrades.
3737
# Increment by one for each non-official patch applied to deps/v8.
38-
'v8_embedder_string': '-node.20',
38+
'v8_embedder_string': '-node.21',
3939

4040
##### V8 defaults for Node.js #####
4141

deps/v8/include/v8.h

+21-10
Original file line numberDiff line numberDiff line change
@@ -7241,10 +7241,10 @@ typedef void (*MicrotasksCompletedCallback)(Isolate*);
72417241
typedef void (*MicrotasksCompletedCallbackWithData)(Isolate*, void*);
72427242
typedef void (*MicrotaskCallback)(void* data);
72437243

7244-
72457244
/**
72467245
* Policy for running microtasks:
7247-
* - explicit: microtasks are invoked with Isolate::RunMicrotasks() method;
7246+
* - explicit: microtasks are invoked with the
7247+
* Isolate::PerformMicrotaskCheckpoint() method;
72487248
* - scoped: microtasks invocation is controlled by MicrotasksScope objects;
72497249
* - auto: microtasks are invoked when the script call depth decrements
72507250
* to zero.
@@ -7335,7 +7335,7 @@ class V8_EXPORT MicrotaskQueue {
73357335
};
73367336

73377337
/**
7338-
* This scope is used to control microtasks when kScopeMicrotasksInvocation
7338+
* This scope is used to control microtasks when MicrotasksPolicy::kScoped
73397339
* is used on Isolate. In this mode every non-primitive call to V8 should be
73407340
* done inside some MicrotasksScope.
73417341
* Microtasks are executed when topmost MicrotasksScope marked as kRunMicrotasks
@@ -8416,10 +8416,13 @@ class V8_EXPORT Isolate {
84168416
* objects are originally built when a WeakRef is created or
84178417
* successfully dereferenced.
84188418
*
8419-
* The embedder is expected to call this when a synchronous sequence
8420-
* of ECMAScript execution completes. It's the embedders
8421-
* responsiblity to make this call at a time which does not
8422-
* interrupt synchronous ECMAScript code execution.
8419+
* This is invoked automatically after microtasks are run. See
8420+
* MicrotasksPolicy for when microtasks are run.
8421+
*
8422+
* This needs to be manually invoked only if the embedder is manually running
8423+
* microtasks via a custom MicrotaskQueue class's PerformCheckpoint. In that
8424+
* case, it is the embedder's responsibility to make this call at a time which
8425+
* does not interrupt synchronous ECMAScript code execution.
84238426
*/
84248427
void ClearKeptObjects();
84258428

@@ -8944,10 +8947,18 @@ class V8_EXPORT Isolate {
89448947
void SetPromiseRejectCallback(PromiseRejectCallback callback);
89458948

89468949
/**
8947-
* Runs the default MicrotaskQueue until it gets empty.
8948-
* Any exceptions thrown by microtask callbacks are swallowed.
8950+
* An alias for PerformMicrotaskCheckpoint.
8951+
*/
8952+
V8_DEPRECATE_SOON("Use PerformMicrotaskCheckpoint.")
8953+
void RunMicrotasks() { PerformMicrotaskCheckpoint(); }
8954+
8955+
/**
8956+
* Runs the default MicrotaskQueue until it gets empty and perform other
8957+
* microtask checkpoint steps, such as calling ClearKeptObjects. Asserts that
8958+
* the MicrotasksPolicy is not kScoped. Any exceptions thrown by microtask
8959+
* callbacks are swallowed.
89498960
*/
8950-
void RunMicrotasks();
8961+
void PerformMicrotaskCheckpoint();
89518962

89528963
/**
89538964
* Enqueues the callback to the default MicrotaskQueue

deps/v8/src/api/api.cc

+12-2
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ class CallDepthScope {
309309
#ifdef V8_CHECK_MICROTASKS_SCOPES_CONSISTENCY
310310
if (do_callback) CheckMicrotasksScopesConsistency(microtask_queue);
311311
#endif
312+
DCHECK(CheckKeptObjectsClearedAfterMicrotaskCheckpoint(microtask_queue));
312313
isolate_->set_next_v8_call_is_safe_for_termination(safe_for_termination_);
313314
}
314315

@@ -323,6 +324,15 @@ class CallDepthScope {
323324
}
324325

325326
private:
327+
bool CheckKeptObjectsClearedAfterMicrotaskCheckpoint(
328+
i::MicrotaskQueue* microtask_queue) {
329+
bool did_perform_microtask_checkpoint =
330+
do_callback && microtask_queue &&
331+
microtask_queue->microtasks_policy() == MicrotasksPolicy::kAuto;
332+
return !did_perform_microtask_checkpoint ||
333+
isolate_->heap()->weak_refs_keep_during_job().IsUndefined(isolate_);
334+
}
335+
326336
i::Isolate* const isolate_;
327337
Local<Context> context_;
328338
bool escaped_;
@@ -8588,10 +8598,10 @@ void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
85888598
isolate->SetPromiseRejectCallback(callback);
85898599
}
85908600

8591-
void Isolate::RunMicrotasks() {
8601+
void Isolate::PerformMicrotaskCheckpoint() {
85928602
DCHECK_NE(MicrotasksPolicy::kScoped, GetMicrotasksPolicy());
85938603
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
8594-
isolate->default_microtask_queue()->RunMicrotasks(isolate);
8604+
isolate->default_microtask_queue()->PerformCheckpoint(this);
85958605
}
85968606

85978607
void Isolate::EnqueueMicrotask(Local<Function> v8_function) {

deps/v8/src/d8/d8.cc

+1-2
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ bool Shell::ExecuteModule(Isolate* isolate, const char* file_name) {
10661066
// able to just busy loop waiting for execution to finish.
10671067
Local<Promise> result_promise(Local<Promise>::Cast(result));
10681068
while (result_promise->State() == Promise::kPending) {
1069-
isolate->RunMicrotasks();
1069+
isolate->PerformMicrotaskCheckpoint();
10701070
}
10711071

10721072
if (result_promise->State() == Promise::kRejected) {
@@ -3297,7 +3297,6 @@ bool ProcessMessages(
32973297
while (v8::platform::PumpMessageLoop(g_default_platform, isolate,
32983298
behavior())) {
32993299
MicrotasksScope::PerformCheckpoint(isolate);
3300-
isolate->ClearKeptObjects();
33013300
}
33023301
if (g_default_platform->IdleTasksEnabled(isolate)) {
33033302
v8::platform::RunIdleTasks(g_default_platform, isolate,

deps/v8/src/execution/microtask-queue.cc

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
115115
!HasMicrotasksSuppressions()) {
116116
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
117117
RunMicrotasks(isolate);
118+
isolate->ClearKeptObjects();
118119
}
119120
}
120121

deps/v8/test/cctest/test-api.cc

+17-17
Original file line numberDiff line numberDiff line change
@@ -19911,7 +19911,7 @@ TEST(RunMicrotasksIgnoresThrownExceptionsFromApi) {
1991119911
CHECK(!isolate->IsExecutionTerminating());
1991219912
isolate->EnqueueMicrotask(ThrowExceptionMicrotask);
1991319913
isolate->EnqueueMicrotask(IncrementCounterMicrotask);
19914-
isolate->RunMicrotasks();
19914+
isolate->PerformMicrotaskCheckpoint();
1991519915
CHECK_EQ(1, microtask_callback_count);
1991619916
CHECK(!try_catch.HasCaught());
1991719917
}
@@ -19953,7 +19953,7 @@ TEST(SetAutorunMicrotasks) {
1995319953
CHECK_EQ(0, CompileRun("ext2Calls")->Int32Value(env.local()).FromJust());
1995419954
CHECK_EQ(1u, microtasks_completed_callback_count);
1995519955

19956-
env->GetIsolate()->RunMicrotasks();
19956+
env->GetIsolate()->PerformMicrotaskCheckpoint();
1995719957
CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value(env.local()).FromJust());
1995819958
CHECK_EQ(1, CompileRun("ext2Calls")->Int32Value(env.local()).FromJust());
1995919959
CHECK_EQ(2u, microtasks_completed_callback_count);
@@ -19965,7 +19965,7 @@ TEST(SetAutorunMicrotasks) {
1996519965
CHECK_EQ(1, CompileRun("ext2Calls")->Int32Value(env.local()).FromJust());
1996619966
CHECK_EQ(2u, microtasks_completed_callback_count);
1996719967

19968-
env->GetIsolate()->RunMicrotasks();
19968+
env->GetIsolate()->PerformMicrotaskCheckpoint();
1996919969
CHECK_EQ(2, CompileRun("ext1Calls")->Int32Value(env.local()).FromJust());
1997019970
CHECK_EQ(2, CompileRun("ext2Calls")->Int32Value(env.local()).FromJust());
1997119971
CHECK_EQ(3u, microtasks_completed_callback_count);
@@ -20015,7 +20015,7 @@ TEST(RunMicrotasksWithoutEnteringContext) {
2001520015
isolate->EnqueueMicrotask(
2001620016
Function::New(context, MicrotaskOne).ToLocalChecked());
2001720017
}
20018-
isolate->RunMicrotasks();
20018+
isolate->PerformMicrotaskCheckpoint();
2001920019
{
2002020020
Context::Scope context_scope(context);
2002120021
CHECK_EQ(1, CompileRun("ext1Calls")->Int32Value(context).FromJust());
@@ -20039,7 +20039,7 @@ static void Regress808911_CurrentContextWrapper(
2003920039
CHECK(isolate->GetCurrentContext() !=
2004020040
isolate->GetEnteredOrMicrotaskContext());
2004120041
isolate->EnqueueMicrotask(Regress808911_MicrotaskCallback, isolate);
20042-
isolate->RunMicrotasks();
20042+
isolate->PerformMicrotaskCheckpoint();
2004320043
}
2004420044

2004520045
THREADED_TEST(Regress808911) {
@@ -22507,7 +22507,7 @@ TEST(PromiseThen) {
2250722507
.ToLocalChecked()
2250822508
->Int32Value(context.local())
2250922509
.FromJust());
22510-
isolate->RunMicrotasks();
22510+
isolate->PerformMicrotaskCheckpoint();
2251122511
CHECK_EQ(1, global->Get(context.local(), v8_str("x1"))
2251222512
.ToLocalChecked()
2251322513
->Int32Value(context.local())
@@ -22533,7 +22533,7 @@ TEST(PromiseThen) {
2253322533
.ToLocalChecked()
2253422534
->Int32Value(context.local())
2253522535
.FromJust());
22536-
isolate->RunMicrotasks();
22536+
isolate->PerformMicrotaskCheckpoint();
2253722537
CHECK_EQ(0, global->Get(context.local(), v8_str("x1"))
2253822538
.ToLocalChecked()
2253922539
->Int32Value(context.local())
@@ -22553,7 +22553,7 @@ TEST(PromiseThen) {
2255322553
.ToLocalChecked()
2255422554
->Int32Value(context.local())
2255522555
.FromJust());
22556-
isolate->RunMicrotasks();
22556+
isolate->PerformMicrotaskCheckpoint();
2255722557
CHECK_EQ(3, global->Get(context.local(), v8_str("x1"))
2255822558
.ToLocalChecked()
2255922559
->Int32Value(context.local())
@@ -22602,7 +22602,7 @@ TEST(PromiseThen2) {
2260222602
.ToLocalChecked()
2260322603
->Int32Value(context.local())
2260422604
.FromJust());
22605-
isolate->RunMicrotasks();
22605+
isolate->PerformMicrotaskCheckpoint();
2260622606
CHECK_EQ(1, global->Get(context.local(), v8_str("x1"))
2260722607
.ToLocalChecked()
2260822608
->Int32Value(context.local())
@@ -22613,7 +22613,7 @@ TEST(PromiseThen2) {
2261322613
.FromJust());
2261422614

2261522615
Local<v8::Promise> b = a->Then(context.local(), f3, f2).ToLocalChecked();
22616-
isolate->RunMicrotasks();
22616+
isolate->PerformMicrotaskCheckpoint();
2261722617
CHECK_EQ(1, global->Get(context.local(), v8_str("x1"))
2261822618
.ToLocalChecked()
2261922619
->Int32Value(context.local())
@@ -22624,7 +22624,7 @@ TEST(PromiseThen2) {
2262422624
.FromJust());
2262522625

2262622626
Local<v8::Promise> c = b->Then(context.local(), f1, f2).ToLocalChecked();
22627-
isolate->RunMicrotasks();
22627+
isolate->PerformMicrotaskCheckpoint();
2262822628
CHECK_EQ(1, global->Get(context.local(), v8_str("x1"))
2262922629
.ToLocalChecked()
2263022630
->Int32Value(context.local())
@@ -22635,7 +22635,7 @@ TEST(PromiseThen2) {
2263522635
.FromJust());
2263622636

2263722637
v8::Local<v8::Promise> d = c->Then(context.local(), f1, f2).ToLocalChecked();
22638-
isolate->RunMicrotasks();
22638+
isolate->PerformMicrotaskCheckpoint();
2263922639
CHECK_EQ(103, global->Get(context.local(), v8_str("x1"))
2264022640
.ToLocalChecked()
2264122641
->Int32Value(context.local())
@@ -22646,7 +22646,7 @@ TEST(PromiseThen2) {
2264622646
.FromJust());
2264722647

2264822648
v8::Local<v8::Promise> e = d->Then(context.local(), f3, f2).ToLocalChecked();
22649-
isolate->RunMicrotasks();
22649+
isolate->PerformMicrotaskCheckpoint();
2265022650
CHECK_EQ(103, global->Get(context.local(), v8_str("x1"))
2265122651
.ToLocalChecked()
2265222652
->Int32Value(context.local())
@@ -22657,7 +22657,7 @@ TEST(PromiseThen2) {
2265722657
.FromJust());
2265822658

2265922659
v8::Local<v8::Promise> f = e->Then(context.local(), f1, f3).ToLocalChecked();
22660-
isolate->RunMicrotasks();
22660+
isolate->PerformMicrotaskCheckpoint();
2266122661
CHECK_EQ(103, global->Get(context.local(), v8_str("x1"))
2266222662
.ToLocalChecked()
2266322663
->Int32Value(context.local())
@@ -22668,7 +22668,7 @@ TEST(PromiseThen2) {
2266822668
.FromJust());
2266922669

2267022670
f->Then(context.local(), f1, f2).ToLocalChecked();
22671-
isolate->RunMicrotasks();
22671+
isolate->PerformMicrotaskCheckpoint();
2267222672
CHECK_EQ(103, global->Get(context.local(), v8_str("x1"))
2267322673
.ToLocalChecked()
2267422674
->Int32Value(context.local())
@@ -25735,7 +25735,7 @@ TEST(DynamicImport) {
2573525735
i::MaybeHandle<i::JSPromise> maybe_promise =
2573625736
i_isolate->RunHostImportModuleDynamicallyCallback(referrer, specifier);
2573725737
i::Handle<i::JSPromise> promise = maybe_promise.ToHandleChecked();
25738-
isolate->RunMicrotasks();
25738+
isolate->PerformMicrotaskCheckpoint();
2573925739
CHECK(result->Equals(i::String::cast(promise->result())));
2574025740
}
2574125741

@@ -26435,7 +26435,7 @@ TEST(MicrotaskContextShouldBeNativeContext) {
2643526435
" await 42;"
2643626436
"})().then(callback);}");
2643726437

26438-
isolate->RunMicrotasks();
26438+
isolate->PerformMicrotaskCheckpoint();
2643926439
}
2644026440

2644126441
TEST(PreviewSetKeysIteratorEntriesWithDeleted) {

deps/v8/test/cctest/test-js-weak-refs.cc

+31-25
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,17 @@ void VerifyWeakCellKeyChain(Isolate* isolate, SimpleNumberDictionary key_map,
158158
va_end(args);
159159
}
160160

161+
Handle<JSWeakRef> MakeWeakRefAndKeepDuringJob(Isolate* isolate) {
162+
HandleScope inner_scope(isolate);
163+
164+
Handle<JSObject> js_object =
165+
isolate->factory()->NewJSObject(isolate->object_function());
166+
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
167+
isolate->heap()->KeepDuringJob(js_object);
168+
169+
return inner_scope.CloseAndEscape(inner_weak_ref);
170+
}
171+
161172
} // namespace
162173

163174
TEST(TestRegister) {
@@ -715,30 +726,35 @@ TEST(TestJSWeakRefKeepDuringJob) {
715726
LocalContext context;
716727

717728
Isolate* isolate = CcTest::i_isolate();
718-
Heap* heap = isolate->heap();
719729
HandleScope outer_scope(isolate);
720-
Handle<JSWeakRef> weak_ref;
721-
{
722-
HandleScope inner_scope(isolate);
723-
724-
Handle<JSObject> js_object =
725-
isolate->factory()->NewJSObject(isolate->object_function());
726-
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
727-
heap->KeepDuringJob(js_object);
730+
Handle<JSWeakRef> weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
731+
CHECK(!weak_ref->target().IsUndefined(isolate));
732+
CcTest::CollectAllGarbage();
733+
CHECK(!weak_ref->target().IsUndefined(isolate));
728734

729-
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
730-
}
735+
// Clears the KeepDuringJob set.
736+
context->GetIsolate()->ClearKeptObjects();
737+
CcTest::CollectAllGarbage();
738+
CHECK(weak_ref->target().IsUndefined(isolate));
731739

740+
weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
741+
CHECK(!weak_ref->target().IsUndefined(isolate));
742+
CcTest::CollectAllGarbage();
732743
CHECK(!weak_ref->target().IsUndefined(isolate));
733744

745+
// ClearKeptObjects should be called by PerformMicrotasksCheckpoint.
746+
CcTest::isolate()->PerformMicrotaskCheckpoint();
734747
CcTest::CollectAllGarbage();
748+
CHECK(weak_ref->target().IsUndefined(isolate));
735749

750+
weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
736751
CHECK(!weak_ref->target().IsUndefined(isolate));
737-
738-
// Clears the KeepDuringJob set.
739-
context->GetIsolate()->ClearKeptObjects();
740752
CcTest::CollectAllGarbage();
753+
CHECK(!weak_ref->target().IsUndefined(isolate));
741754

755+
// ClearKeptObjects should be called by MicrotasksScope::PerformCheckpoint.
756+
v8::MicrotasksScope::PerformCheckpoint(CcTest::isolate());
757+
CcTest::CollectAllGarbage();
742758
CHECK(weak_ref->target().IsUndefined(isolate));
743759
}
744760

@@ -754,17 +770,7 @@ TEST(TestJSWeakRefKeepDuringJobIncrementalMarking) {
754770
Isolate* isolate = CcTest::i_isolate();
755771
Heap* heap = isolate->heap();
756772
HandleScope outer_scope(isolate);
757-
Handle<JSWeakRef> weak_ref;
758-
{
759-
HandleScope inner_scope(isolate);
760-
761-
Handle<JSObject> js_object =
762-
isolate->factory()->NewJSObject(isolate->object_function());
763-
Handle<JSWeakRef> inner_weak_ref = ConstructJSWeakRef(js_object, isolate);
764-
heap->KeepDuringJob(js_object);
765-
766-
weak_ref = inner_scope.CloseAndEscape(inner_weak_ref);
767-
}
773+
Handle<JSWeakRef> weak_ref = MakeWeakRefAndKeepDuringJob(isolate);
768774

769775
CHECK(!weak_ref->target().IsUndefined(isolate));
770776

deps/v8/test/cctest/test-modules.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ TEST(ModuleEvaluationTopLevelAwaitDynamicImport) {
830830
CHECK_EQ(promise->State(), v8::Promise::kPending);
831831
CHECK(!try_catch.HasCaught());
832832

833-
isolate->RunMicrotasks();
833+
isolate->PerformMicrotaskCheckpoint();
834834
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
835835
}
836836
i::FLAG_harmony_top_level_await = previous_top_level_await_flag_value;
@@ -874,7 +874,7 @@ TEST(ModuleEvaluationTopLevelAwaitDynamicImportError) {
874874
CHECK_EQ(promise->State(), v8::Promise::kPending);
875875
CHECK(!try_catch.HasCaught());
876876

877-
isolate->RunMicrotasks();
877+
isolate->PerformMicrotaskCheckpoint();
878878
CHECK_EQ(Module::kErrored, module->GetStatus());
879879
CHECK_EQ(promise->State(), v8::Promise::kRejected);
880880
CHECK(promise->Result()->StrictEquals(v8_str("boom")));

0 commit comments

Comments
 (0)