Skip to content
File

Blob: patches/v8/0013-Implement-cross-request-context-promise-resolve-hand.patch

21.0 KB
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: James M Snell <jasnell@gmail.com>
3Date: Mon, 16 Sep 2024 09:56:04 -0700
4Subject: Implement cross-request context promise resolve handling
5 
6Signed-off-by: James M Snell <jsnell@cloudflare.com>
7 
8diff --git a/BUILD.gn b/BUILD.gn
9index 5d5320c956b322ac9beef18688c9faa0bb10477f..bef571374183084957964816e4d6422ec913df05 100644
10--- a/BUILD.gn
11+++ b/BUILD.gn
12@@ -4638,8 +4638,8 @@ v8_header_set("v8_internal_headers") {
13 "src/tasks/operations-barrier.h",
14 "src/tasks/task-utils.h",
15 "src/torque/runtime-macro-shims.h",
16- "src/tracing/trace-event.h",
17 "src/tracing/trace-event-no-perfetto.h",
18+ "src/tracing/trace-event.h",
19 "src/tracing/trace-id.h",
20 "src/tracing/traced-value.h",
21 "src/tracing/tracing-category-observer.h",
22diff --git a/include/v8-callbacks.h b/include/v8-callbacks.h
23index cfba4bb26f865c0e38574f796200ffc5e0dc60fc..d5d937b0e852066b95a62d7bcf49668205a55391 100644
24--- a/include/v8-callbacks.h
25+++ b/include/v8-callbacks.h
26@@ -536,6 +536,25 @@ using FilterETWSessionByURL2Callback = FilterETWSessionByURLResult (*)(
27 using PromiseCrossContextCallback = MaybeLocal<Promise> (*)(
28 Local<Context> context, Local<Promise> promise, Local<Object> tag);
29
30+/**
31+ * PromiseCrossContextResolveCallback is called when resolving or rejecting a
32+ * pending promise whose context tag is not strictly equal to the isolate's
33+ * current promise context tag. The callback is called with the promise to be
34+ * resolved, its context tag, and a function that when called, causes the
35+ * reactions to the resolved promise to be enqueued. The idea is that the
36+ * embedder sets this callback in the case it needs to defer the actual
37+ * scheduling of the reactions to the given promise to a later time.
38+ * Importantly, when this callback is invoked, the state of the promise
39+ * should have already been updated. We're simply possibly deferring the
40+ * enqueue of the reactions to the promise.
41+ */
42+using PromiseCrossContextResolveCallback = Maybe<void> (*)(
43+ v8::Isolate* isolate, Local<Value> tag, Local<Data> reactions,
44+ Local<Value> argument,
45+ std::function<void(v8::Isolate* isolate, Local<Data> reactions,
46+ Local<Value> argument)>
47+ callback);
48+
49 } // namespace v8
50
51 #endif // INCLUDE_V8_ISOLATE_CALLBACKS_H_
52diff --git a/include/v8-isolate.h b/include/v8-isolate.h
53index 8f620d08c0b8919fc3312c53bd9efa5d11ded1c6..141fece655b6003921452b493f4879baefb9169a 100644
54--- a/include/v8-isolate.h
55+++ b/include/v8-isolate.h
56@@ -1877,6 +1877,8 @@ class V8_EXPORT Isolate {
57
58 class PromiseContextScope;
59 void SetPromiseCrossContextCallback(PromiseCrossContextCallback callback);
60+ void SetPromiseCrossContextResolveCallback(
61+ PromiseCrossContextResolveCallback callback);
62
63 Isolate() = delete;
64 ~Isolate() = delete;
65diff --git a/src/api/api.cc b/src/api/api.cc
66index 877765bf5f57a2953aa2d1e0869ae5db12e8b6b1..178b429ad06ea349bb43dff578b27ae46ae14da7 100644
67--- a/src/api/api.cc
68+++ b/src/api/api.cc
69@@ -12695,7 +12695,13 @@ Isolate::PromiseContextScope::PromiseContextScope(Isolate* isolate,
70 DCHECK(!isolate_->has_promise_context_tag());
71 DCHECK(!tag.IsEmpty());
72 i::Handle<i::Object> handle = Utils::OpenHandle(*tag);
73- isolate_->set_promise_context_tag(*handle);
74+ isolate_->set_promise_context_tag(handle);
75+}
76+
77+void Isolate::SetPromiseCrossContextResolveCallback(
78+ PromiseCrossContextResolveCallback callback) {
79+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
80+ isolate->set_promise_cross_context_resolve_callback(callback);
81 }
82
83 Isolate::PromiseContextScope::~PromiseContextScope() {
84diff --git a/src/builtins/promise-abstract-operations.tq b/src/builtins/promise-abstract-operations.tq
85index 59b8d8d5e243cf46a8093c76613ae2ce420e22e8..838382738236c99b557989dcad53a2ffd32757f7 100644
86--- a/src/builtins/promise-abstract-operations.tq
87+++ b/src/builtins/promise-abstract-operations.tq
88@@ -23,6 +23,9 @@ extern transitioning runtime PromiseRejectEventFromStack(
89
90 extern transitioning runtime PromiseContextCheck(
91 implicit context: Context)(JSPromise): JSPromise;
92+
93+extern transitioning runtime PromiseResolveContextCheck(
94+ implicit context: Context)(JSPromise): JSAny;
95 }
96
97 // https://tc39.es/ecma262/#sec-promise-abstract-operations
98@@ -252,7 +255,8 @@ transitioning builtin RejectPromise(
99 // the runtime handle this operation, which greatly reduces
100 // the complexity here and also avoids a couple of back and
101 // forth between JavaScript and C++ land.
102- if (IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(
103+ if (ToBoolean(runtime::PromiseResolveContextCheck(promise)) ||
104+ IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(
105 promiseHookFlags) ||
106 !promise.HasHandler()) {
107 // 7. If promise.[[PromiseIsHandled]] is false, perform
108diff --git a/src/builtins/promise-resolve.tq b/src/builtins/promise-resolve.tq
109index 202180adbbae91a689a667c40d20b4b1b9cb6edd..c93ac5905d7b349d1c59e9fa86b48662313ea1c3 100644
110--- a/src/builtins/promise-resolve.tq
111+++ b/src/builtins/promise-resolve.tq
112@@ -96,7 +96,9 @@ transitioning builtin ResolvePromise(
113 // We also let the runtime handle it if promise == resolution.
114 // We can use pointer comparison here, since the {promise} is guaranteed
115 // to be a JSPromise inside this function and thus is reference comparable.
116- if (IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
117+
118+ if (ToBoolean(runtime::PromiseResolveContextCheck(promise)) ||
119+ IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate() ||
120 TaggedEqual(promise, resolution))
121 deferred {
122 return runtime::ResolvePromise(promise, resolution);
123diff --git a/src/execution/isolate-inl.h b/src/execution/isolate-inl.h
124index 5e0c1c62b6168e12af1ad067cd57604c17b17ce2..c07ac183137862444753a96a0a80149bf85cc44a 100644
125--- a/src/execution/isolate-inl.h
126+++ b/src/execution/isolate-inl.h
127@@ -133,18 +133,20 @@ bool Isolate::is_execution_terminating() {
128 i::ReadOnlyRoots(this).termination_exception();
129 }
130
131-Tagged<Object> Isolate::promise_context_tag() { return promise_context_tag_; }
132+Handle<Object> Isolate::promise_context_tag() {
133+ return root_handle(RootIndex::kPromiseContextTag);
134+}
135
136 bool Isolate::has_promise_context_tag() {
137- return promise_context_tag_ != ReadOnlyRoots(this).the_hole_value();
138+ return heap()->promise_context_tag() != ReadOnlyRoots(this).the_hole_value();
139 }
140
141 void Isolate::clear_promise_context_tag() {
142- set_promise_context_tag(ReadOnlyRoots(this).the_hole_value());
143+ heap()->set_promise_context_tag(ReadOnlyRoots(this).the_hole_value());
144 }
145
146-void Isolate::set_promise_context_tag(Tagged<Object> tag) {
147- promise_context_tag_ = tag;
148+void Isolate::set_promise_context_tag(Handle<Object> tag) {
149+ heap()->set_promise_context_tag(*tag);
150 }
151
152 void Isolate::set_promise_cross_context_callback(
153@@ -152,6 +154,15 @@ void Isolate::set_promise_cross_context_callback(
154 promise_cross_context_callback_ = callback;
155 }
156
157+void Isolate::set_promise_cross_context_resolve_callback(
158+ PromiseCrossContextResolveCallback callback) {
159+ promise_cross_context_resolve_callback_ = callback;
160+}
161+
162+bool Isolate::has_promise_context_resolve_callback() {
163+ return promise_cross_context_resolve_callback_ != nullptr;
164+}
165+
166 #ifdef DEBUG
167 Tagged<Object> Isolate::VerifyBuiltinsResult(Tagged<Object> result) {
168 if (is_execution_terminating() && !v8_flags.strict_termination_checks) {
169diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc
170index 51666de8200590c2fc26c38090cbed41238ea489..e5b8c171873e461fdd9ba051b4240f5070b5fe86 100644
171--- a/src/execution/isolate.cc
172+++ b/src/execution/isolate.cc
173@@ -629,8 +629,6 @@ void Isolate::Iterate(RootVisitor* v, ThreadLocalTop* thread) {
174 FullObjectSlot(&thread->pending_message_));
175 v->VisitRootPointer(Root::kStackRoots, nullptr,
176 FullObjectSlot(&thread->context_));
177- v->VisitRootPointer(Root::kStackRoots, nullptr,
178- FullObjectSlot(&promise_context_tag_));
179
180 for (v8::TryCatch* block = thread->try_catch_handler_; block != nullptr;
181 block = block->next_) {
182@@ -8438,5 +8436,20 @@ MaybeHandle<JSPromise> Isolate::RunPromiseCrossContextCallback(
183 return v8::Utils::OpenHandle(*result);
184 }
185
186+Maybe<void> Isolate::RunPromiseCrossContextResolveCallback(
187+ v8::Isolate* isolate, Handle<JSObject> tag, DirectHandle<Object> reactions,
188+ DirectHandle<Object> argument, PromiseReaction::Type type) {
189+ CHECK(promise_cross_context_resolve_callback_ != nullptr);
190+ return promise_cross_context_resolve_callback_(
191+ isolate, v8::Utils::ToLocal(tag), v8::Utils::ToLocal(reactions),
192+ v8::Utils::ToLocal(argument),
193+ [type](v8::Isolate* isolate, v8::Local<v8::Data> reactions,
194+ v8::Local<v8::Value> argument) {
195+ JSPromise::ContinueTriggerPromiseReactions(
196+ reinterpret_cast<Isolate*>(isolate), Utils::OpenHandle(*reactions),
197+ Utils::OpenHandle(*argument), type);
198+ });
199+}
200+
201 } // namespace internal
202 } // namespace v8
203diff --git a/src/execution/isolate.h b/src/execution/isolate.h
204index 633f3f8cdef1eceee6edfc921259b7a9895f5a84..bdd57dc4a0eeff42e1918303fca8167414e3cb62 100644
205--- a/src/execution/isolate.h
206+++ b/src/execution/isolate.h
207@@ -45,6 +45,7 @@
208 #include "src/objects/contexts.h"
209 #include "src/objects/debug-objects.h"
210 #include "src/objects/js-objects.h"
211+#include "src/objects/promise.h"
212 #include "src/objects/tagged.h"
213 #include "src/runtime/runtime.h"
214 #include "src/sandbox/code-pointer-table.h"
215@@ -2450,14 +2451,22 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
216 v8::ExceptionContext callback_kind);
217 void SetExceptionPropagationCallback(ExceptionPropagationCallback callback);
218
219- inline Tagged<Object> promise_context_tag();
220+ inline Handle<Object> promise_context_tag();
221 inline bool has_promise_context_tag();
222 inline void clear_promise_context_tag();
223- inline void set_promise_context_tag(Tagged<Object> tag);
224+ inline void set_promise_context_tag(Handle<Object> tag);
225 inline void set_promise_cross_context_callback(
226 PromiseCrossContextCallback callback);
227+ inline void set_promise_cross_context_resolve_callback(
228+ PromiseCrossContextResolveCallback callback);
229 MaybeHandle<JSPromise> RunPromiseCrossContextCallback(
230 Handle<NativeContext> context, Handle<JSPromise> promise);
231+ Maybe<void> RunPromiseCrossContextResolveCallback(
232+ v8::Isolate* isolate, Handle<JSObject> tag,
233+ DirectHandle<Object> reactions, DirectHandle<Object> argument,
234+ PromiseReaction::Type type);
235+
236+ inline bool has_promise_context_resolve_callback();
237
238 #ifdef V8_ENABLE_WASM_SIMD256_REVEC
239 void set_wasm_revec_verifier_for_test(
240@@ -2987,9 +2996,11 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
241
242 bool is_frozen_ = false;
243
244- Tagged<Object> promise_context_tag_;
245- PromiseCrossContextCallback promise_cross_context_callback_;
246+ PromiseCrossContextCallback promise_cross_context_callback_ = nullptr;
247+ PromiseCrossContextResolveCallback promise_cross_context_resolve_callback_ =
248+ nullptr;
249 bool in_promise_cross_context_callback_ = false;
250+ bool in_promise_cross_context_resolve_callback_ = false;
251
252 class PromiseCrossContextCallbackScope;
253
254diff --git a/src/heap/factory.cc b/src/heap/factory.cc
255index 40118ddd5b357d3cdead407ae580c9f5856f13e2..6f40f75197ac0e59daf2bbe1c83192c86fb107ef 100644
256--- a/src/heap/factory.cc
257+++ b/src/heap/factory.cc
258@@ -4857,18 +4857,17 @@ Handle<JSPromise> Factory::NewJSPromiseWithoutHook() {
259 Handle<JSPromise> promise =
260 Cast<JSPromise>(NewJSObject(isolate()->promise_function()));
261 DisallowGarbageCollection no_gc;
262- Tagged<JSPromise> raw = *promise;
263- raw->set_reactions_or_result(Smi::zero(), SKIP_WRITE_BARRIER);
264+ promise->set_reactions_or_result(Smi::zero(), SKIP_WRITE_BARRIER);
265 if (!isolate()->has_promise_context_tag()) {
266- raw->set_context_tag(Smi::zero(), SKIP_WRITE_BARRIER);
267+ promise->set_context_tag(Smi::zero(), SKIP_WRITE_BARRIER);
268 } else {
269- raw->set_context_tag(isolate()->promise_context_tag());
270+ promise->set_context_tag(*isolate()->promise_context_tag());
271 }
272
273- raw->set_flags(0);
274+ promise->set_flags(0);
275 // TODO(v8) remove once embedder data slots are always zero-initialized.
276 InitEmbedderFields(*promise, Smi::zero());
277- DCHECK_EQ(raw->GetEmbedderFieldCount(), v8::Promise::kEmbedderFieldCount);
278+ DCHECK_EQ(promise->GetEmbedderFieldCount(), v8::Promise::kEmbedderFieldCount);
279 return promise;
280 }
281
282diff --git a/src/objects/js-promise.h b/src/objects/js-promise.h
283index fd1f207420aae54ada4ccfebfef1f0345e987af1..d7ce50c130f2e32b0e4ab6fe682ac7e740f5586f 100644
284--- a/src/objects/js-promise.h
285+++ b/src/objects/js-promise.h
286@@ -94,6 +94,11 @@ class JSPromise
287 static_assert(v8::Promise::kFulfilled == 1);
288 static_assert(v8::Promise::kRejected == 2);
289
290+ static void ContinueTriggerPromiseReactions(Isolate* isolate,
291+ DirectHandle<Object> reactions,
292+ DirectHandle<Object> argument,
293+ PromiseReaction::Type type);
294+
295 private:
296 // https://tc39.es/ecma262/#sec-triggerpromisereactions
297 static Handle<Object> TriggerPromiseReactions(Isolate* isolate,
298diff --git a/src/objects/objects.cc b/src/objects/objects.cc
299index ce4beebce1db30e934dede7cc889013a690d1340..081ffe3a15aa21556031d4d6db7951987e8e2ae8 100644
300--- a/src/objects/objects.cc
301+++ b/src/objects/objects.cc
302@@ -4677,6 +4677,22 @@ Handle<Object> JSPromise::Fulfill(DirectHandle<JSPromise> promise,
303 // 6. Set promise.[[PromiseState]] to "fulfilled".
304 promise->set_status(Promise::kFulfilled);
305
306+ Handle<Object> obj(promise->context_tag(), isolate);
307+ bool needs_promise_context_switch =
308+ !(*obj == Smi::zero() ||
309+ obj.is_identical_to(isolate->promise_context_tag()) ||
310+ !isolate->has_promise_context_resolve_callback());
311+ if (needs_promise_context_switch) {
312+ if (isolate
313+ ->RunPromiseCrossContextResolveCallback(
314+ reinterpret_cast<v8::Isolate*>(isolate), Cast<JSObject>(obj),
315+ reactions, value, PromiseReaction::kFulfill)
316+ .IsNothing()) {
317+ return {};
318+ }
319+ return isolate->factory()->undefined_value();
320+ }
321+
322 // 7. Return TriggerPromiseReactions(reactions, value).
323 return TriggerPromiseReactions(isolate, reactions, value,
324 PromiseReaction::kFulfill);
325@@ -4735,6 +4751,22 @@ Handle<Object> JSPromise::Reject(DirectHandle<JSPromise> promise,
326 isolate->ReportPromiseReject(promise, reason, kPromiseRejectWithNoHandler);
327 }
328
329+ Handle<Object> obj(promise->context_tag(), isolate);
330+ bool needs_promise_context_switch =
331+ !(*obj == Smi::zero() ||
332+ obj.is_identical_to(isolate->promise_context_tag()) ||
333+ !isolate->has_promise_context_resolve_callback());
334+ if (needs_promise_context_switch) {
335+ if (isolate
336+ ->RunPromiseCrossContextResolveCallback(
337+ reinterpret_cast<v8::Isolate*>(isolate), Cast<JSObject>(obj),
338+ reactions, reason, PromiseReaction::kReject)
339+ .IsNothing()) {
340+ return {};
341+ }
342+ return isolate->factory()->undefined_value();
343+ }
344+
345 // 8. Return TriggerPromiseReactions(reactions, reason).
346 return TriggerPromiseReactions(isolate, reactions, reason,
347 PromiseReaction::kReject);
348@@ -4843,6 +4875,14 @@ MaybeHandle<Object> JSPromise::Resolve(DirectHandle<JSPromise> promise,
349 }
350
351 // static
352+
353+void JSPromise::ContinueTriggerPromiseReactions(Isolate* isolate,
354+ DirectHandle<Object> reactions,
355+ DirectHandle<Object> argument,
356+ PromiseReaction::Type type) {
357+ TriggerPromiseReactions(isolate, reactions, argument, type);
358+}
359+
360 Handle<Object> JSPromise::TriggerPromiseReactions(
361 Isolate* isolate, DirectHandle<Object> reactions,
362 DirectHandle<Object> argument, PromiseReaction::Type type) {
363diff --git a/src/objects/value-serializer.cc b/src/objects/value-serializer.cc
364index 97b7f51664dda24ffb0c94e4033b2eff2ba4daee..8c0bf0824b200489919f46b18d240c8c5c15a8ec 100644
365--- a/src/objects/value-serializer.cc
366+++ b/src/objects/value-serializer.cc
367@@ -614,11 +614,12 @@ Maybe<bool> ValueSerializer::WriteJSReceiver(
368 }
369 return ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
370 } else if (IsSpecialReceiverInstanceType(instance_type) &&
371- instance_type != JS_SPECIAL_API_OBJECT_TYPE
372+ instance_type != JS_SPECIAL_API_OBJECT_TYPE
373 #if V8_ENABLE_WEBASSEMBLY
374- && instance_type != WASM_STRUCT_TYPE && instance_type != WASM_ARRAY_TYPE
375+ && instance_type != WASM_STRUCT_TYPE &&
376+ instance_type != WASM_ARRAY_TYPE
377 #endif
378- ) {
379+ ) {
380 return ThrowDataCloneError(MessageTemplate::kDataCloneError, receiver);
381 }
382
383diff --git a/src/roots/roots.h b/src/roots/roots.h
384index 47109e31a25db96a56a35a92bf0dabd90e0e42e5..391ad2ebeb504c73e679e80641cbe9b8a2e703a5 100644
385--- a/src/roots/roots.h
386+++ b/src/roots/roots.h
387@@ -427,7 +427,8 @@ class RootVisitor;
388 V(FunctionTemplateInfo, error_stack_getter_fun_template, \
389 ErrorStackGetterSharedFun) \
390 V(FunctionTemplateInfo, error_stack_setter_fun_template, \
391- ErrorStackSetterSharedFun)
392+ ErrorStackSetterSharedFun) \
393+ V(Object, promise_context_tag, PromiseContextTag)
394
395 // Entries in this list are limited to Smis and are not visited during GC.
396 #define SMI_ROOT_LIST(V) \
397diff --git a/src/runtime/runtime-promise.cc b/src/runtime/runtime-promise.cc
398index 896bac667ce40ef23c8c4fcd6174fcd2ebc2076f..0168c239decb00e8f5a722f7e2cb2c0ff41e442d 100644
399--- a/src/runtime/runtime-promise.cc
400+++ b/src/runtime/runtime-promise.cc
401@@ -157,8 +157,10 @@ RUNTIME_FUNCTION(Runtime_RejectPromise) {
402 DirectHandle<JSPromise> promise = args.at<JSPromise>(0);
403 DirectHandle<Object> reason = args.at(1);
404 DirectHandle<Boolean> debug_event = args.at<Boolean>(2);
405- return *JSPromise::Reject(promise, reason,
406- Object::BooleanValue(*debug_event, isolate));
407+ Handle<Object> result = JSPromise::Reject(
408+ promise, reason, Object::BooleanValue(*debug_event, isolate));
409+ RETURN_FAILURE_IF_EXCEPTION(isolate);
410+ return *result;
411 }
412
413 RUNTIME_FUNCTION(Runtime_ResolvePromise) {
414@@ -246,8 +248,8 @@ RUNTIME_FUNCTION(Runtime_PromiseContextInit) {
415 if (!isolate->has_promise_context_tag()) {
416 args.at<JSPromise>(0)->set_context_tag(Smi::zero());
417 } else {
418- CHECK(!IsUndefined(isolate->promise_context_tag()));
419- args.at<JSPromise>(0)->set_context_tag(isolate->promise_context_tag());
420+ CHECK(!IsUndefined(*isolate->promise_context_tag()));
421+ args.at<JSPromise>(0)->set_context_tag(*isolate->promise_context_tag());
422 }
423 return ReadOnlyRoots(isolate).undefined_value();
424 }
425@@ -261,8 +263,9 @@ RUNTIME_FUNCTION(Runtime_PromiseContextCheck) {
426 // If promise.context_tag() is strict equal to isolate.promise_context_tag(),
427 // or if the promise being checked does not have a context tag, we'll just
428 // return promise directly.
429- Tagged<Object> obj = promise->context_tag();
430- if (obj == Smi::zero() || obj == isolate->promise_context_tag()) {
431+ Handle<Object> obj(promise->context_tag(), isolate);
432+ if (*obj == Smi::zero() ||
433+ obj.is_identical_to(isolate->promise_context_tag())) {
434 return *promise;
435 }
436
437@@ -276,5 +279,23 @@ RUNTIME_FUNCTION(Runtime_PromiseContextCheck) {
438 return *result;
439 }
440
441+RUNTIME_FUNCTION(Runtime_PromiseResolveContextCheck) {
442+ HandleScope scope(isolate);
443+ DCHECK_EQ(1, args.length());
444+ Handle<JSPromise> promise = args.at<JSPromise>(0);
445+ // If promise.context_tag() is strict equal to isolate.promise_context_tag(),
446+ // or if the promise being checked does not have a context tag, or if the
447+ // resolve callback has not been set, we'll just return false here to indicate
448+ // that the default handling should be used.
449+ Handle<Object> obj(promise->context_tag(), isolate);
450+ if (*obj == Smi::zero() ||
451+ obj.is_identical_to(isolate->promise_context_tag()) ||
452+ !isolate->has_promise_context_resolve_callback()) {
453+ return isolate->heap()->ToBoolean(false);
454+ }
455+
456+ return isolate->heap()->ToBoolean(true);
457+}
458+
459 } // namespace internal
460 } // namespace v8
461diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
462index 9599b2c393ba3c68ee69d8441b053e6afa23dbfd..1319f166b415c3fe99d0d959615b795df1cf48e0 100644
463--- a/src/runtime/runtime.h
464+++ b/src/runtime/runtime.h
465@@ -449,7 +449,8 @@ constexpr bool CanTriggerGC(T... properties) {
466 F(ConstructAggregateErrorHelper, 4, 1) \
467 F(ConstructInternalAggregateErrorHelper, -1 /* <= 5*/, 1) \
468 F(PromiseContextInit, 1, 1) \
469- F(PromiseContextCheck, 1, 1)
470+ F(PromiseContextCheck, 1, 1) \
471+ F(PromiseResolveContextCheck, 1, 1)
472
473 #define FOR_EACH_INTRINSIC_PROXY(F, I) \
474 F(CheckProxyGetSetTrapResult, 2, 1) \