File
Blob: patches/v8/0013-Implement-cross-request-context-promise-resolve-hand.patch
| 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | From: James M Snell <jasnell@gmail.com> |
| 3 | Date: Mon, 16 Sep 2024 09:56:04 -0700 |
| 4 | Subject: Implement cross-request context promise resolve handling |
| 5 | |
| 6 | Signed-off-by: James M Snell <jsnell@cloudflare.com> |
| 7 | |
| 8 | diff --git a/BUILD.gn b/BUILD.gn |
| 9 | index 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", |
| 22 | diff --git a/include/v8-callbacks.h b/include/v8-callbacks.h |
| 23 | index 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_ |
| 52 | diff --git a/include/v8-isolate.h b/include/v8-isolate.h |
| 53 | index 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; |
| 65 | diff --git a/src/api/api.cc b/src/api/api.cc |
| 66 | index 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() { |
| 84 | diff --git a/src/builtins/promise-abstract-operations.tq b/src/builtins/promise-abstract-operations.tq |
| 85 | index 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 |
| 108 | diff --git a/src/builtins/promise-resolve.tq b/src/builtins/promise-resolve.tq |
| 109 | index 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); |
| 123 | diff --git a/src/execution/isolate-inl.h b/src/execution/isolate-inl.h |
| 124 | index 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) { |
| 169 | diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc |
| 170 | index 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 |
| 203 | diff --git a/src/execution/isolate.h b/src/execution/isolate.h |
| 204 | index 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 | |
| 254 | diff --git a/src/heap/factory.cc b/src/heap/factory.cc |
| 255 | index 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 | |
| 282 | diff --git a/src/objects/js-promise.h b/src/objects/js-promise.h |
| 283 | index 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, |
| 298 | diff --git a/src/objects/objects.cc b/src/objects/objects.cc |
| 299 | index 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) { |
| 363 | diff --git a/src/objects/value-serializer.cc b/src/objects/value-serializer.cc |
| 364 | index 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 | |
| 383 | diff --git a/src/roots/roots.h b/src/roots/roots.h |
| 384 | index 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) \ |
| 397 | diff --git a/src/runtime/runtime-promise.cc b/src/runtime/runtime-promise.cc |
| 398 | index 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 |
| 461 | diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h |
| 462 | index 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) \ |