Promise.cpp (284206B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "builtin/Promise.h" 8 9 #include "mozilla/Atomics.h" 10 #include "mozilla/Maybe.h" 11 #include "mozilla/TimeStamp.h" 12 13 #include "jsapi.h" 14 #include "jsexn.h" 15 #include "jsfriendapi.h" 16 17 #include "js/CallAndConstruct.h" // JS::Construct, JS::IsCallable 18 #include "js/experimental/JitInfo.h" // JSJitGetterOp, JSJitInfo 19 #include "js/ForOfIterator.h" // JS::ForOfIterator 20 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 21 #include "js/PropertySpec.h" 22 #include "js/Stack.h" 23 #include "vm/ArrayObject.h" 24 #include "vm/AsyncFunction.h" 25 #include "vm/AsyncIteration.h" 26 #include "vm/CompletionKind.h" 27 #include "vm/ErrorObject.h" 28 #include "vm/ErrorReporting.h" 29 #include "vm/Iteration.h" 30 #include "vm/JSContext.h" 31 #include "vm/JSObject.h" 32 #include "vm/PlainObject.h" // js::PlainObject 33 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_* 34 #include "vm/SelfHosting.h" 35 #include "vm/Warnings.h" // js::WarnNumberASCII 36 37 #include "debugger/DebugAPI-inl.h" 38 #include "gc/StableCellHasher-inl.h" 39 #include "vm/Compartment-inl.h" 40 #include "vm/ErrorObject-inl.h" 41 #include "vm/JSContext-inl.h" // JSContext::check 42 #include "vm/JSObject-inl.h" 43 #include "vm/NativeObject-inl.h" 44 45 using namespace js; 46 47 static double MillisecondsSinceStartup() { 48 auto now = mozilla::TimeStamp::Now(); 49 return (now - mozilla::TimeStamp::FirstTimeStamp()).ToMilliseconds(); 50 } 51 52 constexpr auto HostDefinedDataIsOptimizedOut = nullptr; 53 54 enum ResolutionMode { ResolveMode, RejectMode }; 55 56 /** 57 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 58 * 59 * Promise Resolve Functions 60 * https://tc39.es/ecma262/#sec-promise-resolve-functions 61 */ 62 enum ResolveFunctionSlots { 63 // NOTE: All slot represent [[AlreadyResolved]].[[Value]]. 64 // 65 // The spec creates single record for [[AlreadyResolved]] and shares it 66 // between Promise Resolve Function and Promise Reject Function. 67 // 68 // Step 1. Let alreadyResolved be the Record { [[Value]]: false }. 69 // ... 70 // Step 6. Set resolve.[[AlreadyResolved]] to alreadyResolved. 71 // ... 72 // Step 11. Set reject.[[AlreadyResolved]] to alreadyResolved. 73 // 74 // We implement it by clearing all slots, both in 75 // Promise Resolve Function and Promise Reject Function at the same time. 76 // 77 // If none of slots are undefined, [[AlreadyResolved]].[[Value]] is false. 78 // If all slot are undefined, [[AlreadyResolved]].[[Value]] is true. 79 80 // [[Promise]] slot. 81 // A possibly-wrapped promise. 82 ResolveFunctionSlot_Promise = 0, 83 84 // The corresponding Promise Reject Function. 85 ResolveFunctionSlot_RejectFunction, 86 }; 87 88 /** 89 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 90 * 91 * Promise Reject Functions 92 * https://tc39.es/ecma262/#sec-promise-reject-functions 93 */ 94 enum RejectFunctionSlots { 95 // [[Promise]] slot. 96 // A possibly-wrapped promise. 97 RejectFunctionSlot_Promise = 0, 98 99 // The corresponding Promise Resolve Function. 100 RejectFunctionSlot_ResolveFunction, 101 }; 102 103 // The promise combinator builtins such as Promise.all and Promise.allSettled 104 // allocate one or two functions for each array element. These functions store 105 // some state in extended slots. 106 enum PromiseCombinatorElementFunctionSlots { 107 // This slot stores either: 108 // 109 // - The [[Index]] slot (the array index) as Int32Value. 110 // 111 // - For the onRejected functions for Promise.allSettled, a pointer to the 112 // corresponding onFulfilled function stored as ObjectValue. In this case 113 // the slots on that function must be used instead because the 114 // [[AlreadyCalled]] flag must be shared by these two functions. 115 PromiseCombinatorElementFunctionSlot_ElementIndexOrResolveFunc = 0, 116 117 // This slot stores a pointer to the PromiseCombinatorDataHolder JS object. 118 // It's also used to represent the [[AlreadyCalled]] flag: we set this slot to 119 // UndefinedValue when [[AlreadyCalled]] is set to true in the spec. 120 // 121 // The onRejected functions for Promise.allSettled have a NullValue stored in 122 // this slot. In this case the slot shouldn't be used because the 123 // [[AlreadyCalled]] state must be shared by the two functions. 124 PromiseCombinatorElementFunctionSlot_Data 125 }; 126 127 enum ReactionJobSlots { 128 ReactionJobSlot_ReactionRecord = 0, 129 }; 130 131 // Extended function slots used to pass arguments through to either 132 // PromiseResolveThenableJob, or PromiseResolveBuiltinThenableJob when calling 133 // the built-in `then`. 134 enum ThenableJobSlots { 135 // The Promise to resolve using the given thenable. 136 // 137 // This can be a CCW when used for PromiseResolveThenableJob, otherwise it is 138 // guaranteed not to be. 139 ThenableJobSlot_Promise = 0, 140 141 // The thenable to use as the receiver when calling the `then` function. 142 // 143 // This can be a CCW when used for PromiseResolveThenableJob, otherwise it is 144 // guaranteed not to be. 145 ThenableJobSlot_Thenable, 146 147 // The handler to use as the Promise reaction, when not calling the built-in 148 // `then`. It is a callable object that's guaranteed to be from the same 149 // compartment as the PromiseReactionJob. 150 ThenableJobSlot_Handler, 151 152 ThenableJobSlot_Count 153 }; 154 155 static_assert(size_t(ThenableJobSlot_Count) <= 156 size_t(FunctionExtended::SlotCount)); 157 158 struct PromiseCapability { 159 JSObject* promise = nullptr; 160 JSObject* resolve = nullptr; 161 JSObject* reject = nullptr; 162 163 PromiseCapability() = default; 164 165 void trace(JSTracer* trc); 166 }; 167 168 void PromiseCapability::trace(JSTracer* trc) { 169 if (promise) { 170 TraceRoot(trc, &promise, "PromiseCapability::promise"); 171 } 172 if (resolve) { 173 TraceRoot(trc, &resolve, "PromiseCapability::resolve"); 174 } 175 if (reject) { 176 TraceRoot(trc, &reject, "PromiseCapability::reject"); 177 } 178 } 179 180 namespace js { 181 182 template <typename Wrapper> 183 class WrappedPtrOperations<PromiseCapability, Wrapper> { 184 const PromiseCapability& capability() const { 185 return static_cast<const Wrapper*>(this)->get(); 186 } 187 188 public: 189 HandleObject promise() const { 190 return HandleObject::fromMarkedLocation(&capability().promise); 191 } 192 HandleObject resolve() const { 193 return HandleObject::fromMarkedLocation(&capability().resolve); 194 } 195 HandleObject reject() const { 196 return HandleObject::fromMarkedLocation(&capability().reject); 197 } 198 }; 199 200 template <typename Wrapper> 201 class MutableWrappedPtrOperations<PromiseCapability, Wrapper> 202 : public WrappedPtrOperations<PromiseCapability, Wrapper> { 203 PromiseCapability& capability() { return static_cast<Wrapper*>(this)->get(); } 204 205 public: 206 MutableHandleObject promise() { 207 return MutableHandleObject::fromMarkedLocation(&capability().promise); 208 } 209 MutableHandleObject resolve() { 210 return MutableHandleObject::fromMarkedLocation(&capability().resolve); 211 } 212 MutableHandleObject reject() { 213 return MutableHandleObject::fromMarkedLocation(&capability().reject); 214 } 215 }; 216 217 } // namespace js 218 219 struct PromiseCombinatorElements; 220 221 class PromiseCombinatorDataHolder : public NativeObject { 222 enum { 223 Slot_Promise = 0, 224 Slot_RemainingElements, 225 Slot_ValuesArray, 226 Slot_ResolveOrRejectFunction, 227 SlotsCount, 228 }; 229 230 public: 231 static const JSClass class_; 232 JSObject* promiseObj() { return &getFixedSlot(Slot_Promise).toObject(); } 233 JSObject* resolveOrRejectObj() { 234 return &getFixedSlot(Slot_ResolveOrRejectFunction).toObject(); 235 } 236 Value valuesArray() { return getFixedSlot(Slot_ValuesArray); } 237 int32_t remainingCount() { 238 return getFixedSlot(Slot_RemainingElements).toInt32(); 239 } 240 int32_t increaseRemainingCount() { 241 int32_t remainingCount = getFixedSlot(Slot_RemainingElements).toInt32(); 242 remainingCount++; 243 setFixedSlot(Slot_RemainingElements, Int32Value(remainingCount)); 244 return remainingCount; 245 } 246 int32_t decreaseRemainingCount() { 247 int32_t remainingCount = getFixedSlot(Slot_RemainingElements).toInt32(); 248 remainingCount--; 249 MOZ_ASSERT(remainingCount >= 0, "unpaired calls to decreaseRemainingCount"); 250 setFixedSlot(Slot_RemainingElements, Int32Value(remainingCount)); 251 return remainingCount; 252 } 253 254 static PromiseCombinatorDataHolder* New( 255 JSContext* cx, HandleObject resultPromise, 256 Handle<PromiseCombinatorElements> elements, HandleObject resolveOrReject); 257 }; 258 259 const JSClass PromiseCombinatorDataHolder::class_ = { 260 "PromiseCombinatorDataHolder", 261 JSCLASS_HAS_RESERVED_SLOTS(SlotsCount), 262 }; 263 264 // Smart pointer to the "F.[[Values]]" part of the state of a Promise.all or 265 // Promise.allSettled invocation, or the "F.[[Errors]]" part of the state of a 266 // Promise.any invocation. Copes with compartment issues when setting an 267 // element. 268 struct MOZ_STACK_CLASS PromiseCombinatorElements final { 269 // Object value holding the elements array. The object can be a wrapper. 270 Value value; 271 272 // Unwrapped elements array. May not belong to the current compartment! 273 ArrayObject* unwrappedArray = nullptr; 274 275 // Set to true if the |setElement| method needs to wrap its input value. 276 bool setElementNeedsWrapping = false; 277 278 PromiseCombinatorElements() = default; 279 280 void trace(JSTracer* trc); 281 }; 282 283 void PromiseCombinatorElements::trace(JSTracer* trc) { 284 TraceRoot(trc, &value, "PromiseCombinatorElements::value"); 285 if (unwrappedArray) { 286 TraceRoot(trc, &unwrappedArray, 287 "PromiseCombinatorElements::unwrappedArray"); 288 } 289 } 290 291 namespace js { 292 293 template <typename Wrapper> 294 class WrappedPtrOperations<PromiseCombinatorElements, Wrapper> { 295 const PromiseCombinatorElements& elements() const { 296 return static_cast<const Wrapper*>(this)->get(); 297 } 298 299 public: 300 HandleValue value() const { 301 return HandleValue::fromMarkedLocation(&elements().value); 302 } 303 304 Handle<ArrayObject*> unwrappedArray() const { 305 return Handle<ArrayObject*>::fromMarkedLocation(&elements().unwrappedArray); 306 } 307 }; 308 309 template <typename Wrapper> 310 class MutableWrappedPtrOperations<PromiseCombinatorElements, Wrapper> 311 : public WrappedPtrOperations<PromiseCombinatorElements, Wrapper> { 312 PromiseCombinatorElements& elements() { 313 return static_cast<Wrapper*>(this)->get(); 314 } 315 316 public: 317 MutableHandleValue value() { 318 return MutableHandleValue::fromMarkedLocation(&elements().value); 319 } 320 321 MutableHandle<ArrayObject*> unwrappedArray() { 322 return MutableHandle<ArrayObject*>::fromMarkedLocation( 323 &elements().unwrappedArray); 324 } 325 326 void initialize(ArrayObject* arrayObj) { 327 unwrappedArray().set(arrayObj); 328 value().setObject(*arrayObj); 329 330 // |needsWrapping| isn't tracked here, because all modifications on the 331 // initial elements don't require any wrapping. 332 } 333 334 void initialize(PromiseCombinatorDataHolder* data, ArrayObject* arrayObj, 335 bool needsWrapping) { 336 unwrappedArray().set(arrayObj); 337 value().set(data->valuesArray()); 338 elements().setElementNeedsWrapping = needsWrapping; 339 } 340 341 [[nodiscard]] bool pushUndefined(JSContext* cx) { 342 // Helper for the AutoRealm we need to work with |array|. We mostly do this 343 // for performance; we could go ahead and do the define via a cross- 344 // compartment proxy instead... 345 AutoRealm ar(cx, unwrappedArray()); 346 347 Handle<ArrayObject*> arrayObj = unwrappedArray(); 348 return js::NewbornArrayPush(cx, arrayObj, UndefinedValue()); 349 } 350 351 // `Promise.all` Resolve Element Functions 352 // Step 9. Set values[index] to x. 353 // 354 // `Promise.allSettled` Resolve Element Functions 355 // `Promise.allSettled` Reject Element Functions 356 // Step 12. Set values[index] to obj. 357 // 358 // `Promise.any` Reject Element Functions 359 // Step 9. Set errors[index] to x. 360 // 361 // These handler functions are always created in the compartment of the 362 // Promise.all/allSettled/any function, which isn't necessarily the same 363 // compartment as unwrappedArray as explained in NewPromiseCombinatorElements. 364 // So before storing |val| we may need to enter unwrappedArray's compartment. 365 [[nodiscard]] bool setElement(JSContext* cx, uint32_t index, 366 HandleValue val) { 367 // The index is guaranteed to be initialized to `undefined`. 368 MOZ_ASSERT(unwrappedArray()->getDenseElement(index).isUndefined()); 369 370 if (elements().setElementNeedsWrapping) { 371 AutoRealm ar(cx, unwrappedArray()); 372 373 RootedValue rootedVal(cx, val); 374 if (!cx->compartment()->wrap(cx, &rootedVal)) { 375 return false; 376 } 377 unwrappedArray()->setDenseElement(index, rootedVal); 378 } else { 379 unwrappedArray()->setDenseElement(index, val); 380 } 381 return true; 382 } 383 }; 384 385 } // namespace js 386 387 PromiseCombinatorDataHolder* PromiseCombinatorDataHolder::New( 388 JSContext* cx, HandleObject resultPromise, 389 Handle<PromiseCombinatorElements> elements, HandleObject resolveOrReject) { 390 auto* dataHolder = NewBuiltinClassInstance<PromiseCombinatorDataHolder>(cx); 391 if (!dataHolder) { 392 return nullptr; 393 } 394 395 cx->check(resultPromise, elements.value(), resolveOrReject); 396 397 dataHolder->initFixedSlot(Slot_Promise, ObjectValue(*resultPromise)); 398 dataHolder->initFixedSlot(Slot_RemainingElements, Int32Value(1)); 399 dataHolder->initFixedSlot(Slot_ValuesArray, elements.value()); 400 dataHolder->initFixedSlot(Slot_ResolveOrRejectFunction, 401 ObjectValue(*resolveOrReject)); 402 return dataHolder; 403 } 404 405 namespace { 406 // Generator used by PromiseObject::getID. 407 mozilla::Atomic<uint64_t> gIDGenerator(0); 408 } // namespace 409 410 // Returns true if the following properties haven't been mutated: 411 // - On the original Promise.prototype object: "constructor" and "then" 412 // - On the original Promise constructor: "resolve" and @@species 413 static bool HasDefaultPromiseProperties(JSContext* cx) { 414 return cx->realm()->realmFuses.optimizePromiseLookupFuse.intact(); 415 } 416 417 static bool IsPromiseWithDefaultProperties(PromiseObject* promise, 418 JSContext* cx) { 419 if (!HasDefaultPromiseProperties(cx)) { 420 return false; 421 } 422 423 // Ensure the promise's prototype is the original Promise.prototype object. 424 JSObject* proto = cx->global()->maybeGetPrototype(JSProto_Promise); 425 if (!proto || promise->staticPrototype() != proto) { 426 return false; 427 } 428 429 // Ensure `promise` doesn't define any own properties. This serves as a 430 // quick check to make sure `promise` doesn't define an own "constructor" 431 // or "then" property which may shadow Promise.prototype.constructor or 432 // Promise.prototype.then. 433 return promise->empty(); 434 } 435 436 class PromiseDebugInfo : public NativeObject { 437 private: 438 enum Slots { 439 Slot_AllocationSite, 440 Slot_ResolutionSite, 441 Slot_AllocationTime, 442 Slot_ResolutionTime, 443 Slot_Id, 444 SlotCount 445 }; 446 447 public: 448 static const JSClass class_; 449 static PromiseDebugInfo* create(JSContext* cx, 450 Handle<PromiseObject*> promise) { 451 Rooted<PromiseDebugInfo*> debugInfo( 452 cx, NewBuiltinClassInstance<PromiseDebugInfo>(cx)); 453 if (!debugInfo) { 454 return nullptr; 455 } 456 457 RootedObject stack(cx); 458 if (!JS::CaptureCurrentStack(cx, &stack, 459 JS::StackCapture(JS::AllFrames()))) { 460 return nullptr; 461 } 462 debugInfo->setFixedSlot(Slot_AllocationSite, ObjectOrNullValue(stack)); 463 debugInfo->setFixedSlot(Slot_ResolutionSite, NullValue()); 464 debugInfo->setFixedSlot(Slot_AllocationTime, 465 DoubleValue(MillisecondsSinceStartup())); 466 debugInfo->setFixedSlot(Slot_ResolutionTime, NumberValue(0)); 467 promise->setFixedSlot(PromiseSlot_DebugInfo, ObjectValue(*debugInfo)); 468 469 return debugInfo; 470 } 471 472 static PromiseDebugInfo* FromPromise(PromiseObject* promise) { 473 Value val = promise->getFixedSlot(PromiseSlot_DebugInfo); 474 if (val.isObject()) { 475 return &val.toObject().as<PromiseDebugInfo>(); 476 } 477 return nullptr; 478 } 479 480 /** 481 * Returns the given PromiseObject's process-unique ID. 482 * The ID is lazily assigned when first queried, and then either stored 483 * in the DebugInfo slot if no debug info was recorded for this Promise, 484 * or in the Id slot of the DebugInfo object. 485 */ 486 static uint64_t id(PromiseObject* promise) { 487 Value idVal(promise->getFixedSlot(PromiseSlot_DebugInfo)); 488 if (idVal.isUndefined()) { 489 idVal.setDouble(++gIDGenerator); 490 promise->setFixedSlot(PromiseSlot_DebugInfo, idVal); 491 } else if (idVal.isObject()) { 492 PromiseDebugInfo* debugInfo = FromPromise(promise); 493 idVal = debugInfo->getFixedSlot(Slot_Id); 494 if (idVal.isUndefined()) { 495 idVal.setDouble(++gIDGenerator); 496 debugInfo->setFixedSlot(Slot_Id, idVal); 497 } 498 } 499 return uint64_t(idVal.toNumber()); 500 } 501 502 double allocationTime() { 503 return getFixedSlot(Slot_AllocationTime).toNumber(); 504 } 505 double resolutionTime() { 506 return getFixedSlot(Slot_ResolutionTime).toNumber(); 507 } 508 JSObject* allocationSite() { 509 return getFixedSlot(Slot_AllocationSite).toObjectOrNull(); 510 } 511 JSObject* resolutionSite() { 512 return getFixedSlot(Slot_ResolutionSite).toObjectOrNull(); 513 } 514 515 // The |unwrappedRejectionStack| parameter should only be set on promise 516 // rejections and should be the stack of the exception that caused the promise 517 // to be rejected. If the |unwrappedRejectionStack| is null, the current stack 518 // will be used instead. This is also the default behavior for fulfilled 519 // promises. 520 static void setResolutionInfo(JSContext* cx, Handle<PromiseObject*> promise, 521 Handle<SavedFrame*> unwrappedRejectionStack) { 522 MOZ_ASSERT_IF(unwrappedRejectionStack, 523 promise->state() == JS::PromiseState::Rejected); 524 525 if (!JS::IsAsyncStackCaptureEnabledForRealm(cx)) { 526 return; 527 } 528 529 // If async stacks weren't enabled and the Promise's global wasn't a 530 // debuggee when the Promise was created, we won't have a debugInfo 531 // object. We still want to capture the resolution stack, so we 532 // create the object now and change it's slots' values around a bit. 533 Rooted<PromiseDebugInfo*> debugInfo(cx, FromPromise(promise)); 534 if (!debugInfo) { 535 RootedValue idVal(cx, promise->getFixedSlot(PromiseSlot_DebugInfo)); 536 debugInfo = create(cx, promise); 537 if (!debugInfo) { 538 cx->clearPendingException(); 539 return; 540 } 541 542 // The current stack was stored in the AllocationSite slot, move 543 // it to ResolutionSite as that's what it really is. 544 debugInfo->setFixedSlot(Slot_ResolutionSite, 545 debugInfo->getFixedSlot(Slot_AllocationSite)); 546 debugInfo->setFixedSlot(Slot_AllocationSite, NullValue()); 547 548 // There's no good default for a missing AllocationTime, so 549 // instead of resetting that, ensure that it's the same as 550 // ResolutionTime, so that the diff shows as 0, which isn't great, 551 // but bearable. 552 debugInfo->setFixedSlot(Slot_ResolutionTime, 553 debugInfo->getFixedSlot(Slot_AllocationTime)); 554 555 // The Promise's ID might've been queried earlier, in which case 556 // it's stored in the DebugInfo slot. We saved that earlier, so 557 // now we can store it in the right place (or leave it as 558 // undefined if it wasn't ever initialized.) 559 debugInfo->setFixedSlot(Slot_Id, idVal); 560 return; 561 } 562 563 RootedObject stack(cx, unwrappedRejectionStack); 564 if (stack) { 565 // The exception stack is always unwrapped so it might be in 566 // a different compartment. 567 if (!cx->compartment()->wrap(cx, &stack)) { 568 cx->clearPendingException(); 569 return; 570 } 571 } else { 572 if (!JS::CaptureCurrentStack(cx, &stack, 573 JS::StackCapture(JS::AllFrames()))) { 574 cx->clearPendingException(); 575 return; 576 } 577 } 578 579 debugInfo->setFixedSlot(Slot_ResolutionSite, ObjectOrNullValue(stack)); 580 debugInfo->setFixedSlot(Slot_ResolutionTime, 581 DoubleValue(MillisecondsSinceStartup())); 582 } 583 584 #if defined(DEBUG) || defined(JS_JITSPEW) 585 void dumpOwnFields(js::JSONPrinter& json) const; 586 #endif 587 }; 588 589 const JSClass PromiseDebugInfo::class_ = { 590 "PromiseDebugInfo", 591 JSCLASS_HAS_RESERVED_SLOTS(SlotCount), 592 }; 593 594 double PromiseObject::allocationTime() { 595 auto debugInfo = PromiseDebugInfo::FromPromise(this); 596 if (debugInfo) { 597 return debugInfo->allocationTime(); 598 } 599 return 0; 600 } 601 602 double PromiseObject::resolutionTime() { 603 auto debugInfo = PromiseDebugInfo::FromPromise(this); 604 if (debugInfo) { 605 return debugInfo->resolutionTime(); 606 } 607 return 0; 608 } 609 610 JSObject* PromiseObject::allocationSite() { 611 auto debugInfo = PromiseDebugInfo::FromPromise(this); 612 if (debugInfo) { 613 return debugInfo->allocationSite(); 614 } 615 return nullptr; 616 } 617 618 JSObject* PromiseObject::resolutionSite() { 619 auto debugInfo = PromiseDebugInfo::FromPromise(this); 620 if (debugInfo) { 621 JSObject* site = debugInfo->resolutionSite(); 622 if (site && !JS_IsDeadWrapper(site)) { 623 MOZ_ASSERT(UncheckedUnwrap(site)->is<SavedFrame>()); 624 return site; 625 } 626 } 627 return nullptr; 628 } 629 630 /** 631 * Wrapper for GetAndClearExceptionAndStack that handles cases where 632 * no exception is pending, but an error occurred. 633 * This can be the case if an OOM was encountered while throwing the error. 634 */ 635 static bool MaybeGetAndClearExceptionAndStack( 636 JSContext* cx, MutableHandleValue rval, MutableHandle<SavedFrame*> stack) { 637 if (!cx->isExceptionPending()) { 638 return false; 639 } 640 641 return GetAndClearExceptionAndStack(cx, rval, stack); 642 } 643 644 [[nodiscard]] static bool CallPromiseRejectFunction( 645 JSContext* cx, HandleObject rejectFun, HandleValue reason, 646 HandleObject promiseObj, Handle<SavedFrame*> unwrappedRejectionStack, 647 UnhandledRejectionBehavior behavior); 648 649 /** 650 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 651 * 652 * IfAbruptRejectPromise ( value, capability ) 653 * https://tc39.es/ecma262/#sec-ifabruptrejectpromise 654 * 655 * Steps 1.a-b. 656 * 657 * Extracting all of this internal spec algorithm into a helper function would 658 * be tedious, so the check in step 1 and the entirety of step 2 aren't 659 * included. 660 */ 661 bool js::AbruptRejectPromise(JSContext* cx, CallArgs& args, 662 HandleObject promiseObj, HandleObject reject) { 663 // Step 1.a. Perform 664 // ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). 665 RootedValue reason(cx); 666 Rooted<SavedFrame*> stack(cx); 667 if (!MaybeGetAndClearExceptionAndStack(cx, &reason, &stack)) { 668 return false; 669 } 670 671 if (!CallPromiseRejectFunction(cx, reject, reason, promiseObj, stack, 672 UnhandledRejectionBehavior::Report)) { 673 return false; 674 } 675 676 // Step 1.b. Return capability.[[Promise]]. 677 args.rval().setObject(*promiseObj); 678 return true; 679 } 680 681 static bool AbruptRejectPromise(JSContext* cx, CallArgs& args, 682 Handle<PromiseCapability> capability) { 683 return AbruptRejectPromise(cx, args, capability.promise(), 684 capability.reject()); 685 } 686 687 class MicroTaskEntry : public NativeObject { 688 protected: 689 enum Slots { 690 // Shared slots: 691 Promise = 0, // see comment in PromiseReactionRecord 692 HostDefinedData, // See comment in PromiseReactionRecord 693 694 // Only needed for microtask jobs 695 AllocationStack, 696 HostDefinedGlobalRepresentative, 697 SlotCount, 698 }; 699 700 public: 701 JSObject* promise() const { 702 return getFixedSlot(Slots::Promise).toObjectOrNull(); 703 } 704 705 void setPromise(JSObject* obj) { 706 setFixedSlot(Slots::Promise, ObjectOrNullValue(obj)); 707 } 708 709 Value getHostDefinedData() const { 710 return getFixedSlot(Slots::HostDefinedData); 711 } 712 713 void setHostDefinedData(const Value& val) { 714 setFixedSlot(Slots::HostDefinedData, val); 715 } 716 717 JSObject* allocationStack() const { 718 return getFixedSlot(Slots::AllocationStack).toObjectOrNull(); 719 } 720 721 void setAllocationStack(JSObject* stack) { 722 setFixedSlot(Slots::AllocationStack, ObjectOrNullValue(stack)); 723 } 724 725 JSObject* hostDefinedGlobalRepresentative() const { 726 Value v = getFixedSlot(Slots::HostDefinedGlobalRepresentative); 727 return v.isObjectOrNull() ? v.toObjectOrNull() : nullptr; 728 } 729 730 void setHostDefinedGlobalRepresentative(JSObject* global) { 731 setFixedSlot(Slots::HostDefinedGlobalRepresentative, 732 ObjectOrNullValue(global)); 733 } 734 }; 735 736 /** 737 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 738 * 739 * PromiseReaction Records 740 * https://tc39.es/ecma262/#sec-promisereaction-records 741 */ 742 class PromiseReactionRecord : public MicroTaskEntry { 743 // If this flag is set, this reaction record is already enqueued to the 744 // job queue, and the spec's [[Type]] field is represented by 745 // REACTION_FLAG_FULFILLED flag. 746 // 747 // If this flag isn't yet set, [[Type]] field is undefined. 748 static constexpr uint32_t REACTION_FLAG_RESOLVED = 0x1; 749 750 // This bit is valid only when REACTION_FLAG_RESOLVED flag is set. 751 // 752 // If this flag is set, [[Type]] field is Fulfill. 753 // If this flag isn't set, [[Type]] field is Reject. 754 static constexpr uint32_t REACTION_FLAG_FULFILLED = 0x2; 755 756 // If this flag is set, this reaction record is created for resolving 757 // one promise P1 to another promise P2, and 758 // Slot::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator slot 759 // holds P2. 760 static constexpr uint32_t REACTION_FLAG_DEFAULT_RESOLVING_HANDLER = 0x4; 761 762 // If this flag is set, this reaction record is created for async function 763 // and Slot::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator 764 // slot holds internal generator object of the async function. 765 static constexpr uint32_t REACTION_FLAG_ASYNC_FUNCTION = 0x8; 766 767 // If this flag is set, this reaction record is created for async generator 768 // and Slot::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator 769 // slot holds the async generator object of the async generator. 770 static constexpr uint32_t REACTION_FLAG_ASYNC_GENERATOR = 0x10; 771 772 // If this flag is set, this reaction record is created only for providing 773 // information to debugger. 774 static constexpr uint32_t REACTION_FLAG_DEBUGGER_DUMMY = 0x20; 775 776 // This bit is valid only when the promise object is optimized out 777 // for the reaction. 778 // 779 // If this flag is set, unhandled rejection should be ignored. 780 // Otherwise, promise object should be created on-demand for unhandled 781 // rejection. 782 static constexpr uint32_t REACTION_FLAG_IGNORE_UNHANDLED_REJECTION = 0x40; 783 784 // If this flag is set, this reaction record is created for async-from-sync 785 // iterators and 786 // Slot::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator slot 787 // holds the async-from-sync iterator object. 788 static constexpr uint32_t REACTION_FLAG_ASYNC_FROM_SYNC_ITERATOR = 0x80; 789 790 public: 791 enum Slots { 792 // This is the promise-like object that gets resolved with the result of 793 // this reaction, if any. If this reaction record was created with .then or 794 // .catch, this is the promise that .then or .catch returned. 795 // 796 // The spec says that a PromiseReaction record has a [[Capability]] field 797 // whose value is either undefined or a PromiseCapability record, but we 798 // just store the PromiseCapability's fields directly in this object. This 799 // is the 800 // capability's [[Promise]] field; its [[Resolve]] and [[Reject]] fields are 801 // stored in Slot::Resolve and Slot::Reject. 802 // 803 // This can be 'null' in reaction records created for a few situations: 804 // 805 // - When you resolve one promise to another. When you pass a promise P1 to 806 // the 'fulfill' function of a promise P2, so that resolving P1 resolves 807 // P2 in the same way, P1 gets a reaction record with the 808 // REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag set and whose 809 // Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator 810 // slot holds P2. 811 // 812 // - When you await a promise. When an async function or generator awaits a 813 // value V, then the await expression generates an internal promise P, 814 // resolves it to V, and then gives P a reaction record with the 815 // REACTION_FLAG_ASYNC_FUNCTION or REACTION_FLAG_ASYNC_GENERATOR flag set 816 // and whose Slot::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator 817 // slot holds the generator object. (Typically V is a promise, so 818 // resolving P to V gives V a REACTION_FLAGS_DEFAULT_RESOLVING_HANDLER 819 // reaction 820 // record as described above.) 821 // 822 // - When JS::AddPromiseReactions{,IgnoringUnhandledRejection} cause the 823 // reaction to be created. (These functions act as if they had created a 824 // promise to invoke the appropriate provided reaction function, without 825 // actually allocating a promise for them.) 826 Promise = MicroTaskEntry::Slots::Promise, 827 828 // The host defined data for this reaction record. Can be null. 829 // See step 5 in https://html.spec.whatwg.org/#hostmakejobcallback 830 HostDefinedData = MicroTaskEntry::Slots::HostDefinedData, 831 832 // < Invisibly here are the microtask job slots from the parent class 833 // MicroTask. > 834 835 // A slot holding an object from the realm where we need to execute 836 // the reaction job. This may be a CCW. We don't store the global 837 // of the realm directly because wrappers to globals can change 838 // globals, which breaks code. 839 EnqueueGlobalRepresentative = MicroTaskEntry::Slots::SlotCount, 840 841 // The [[Handler]] field(s) of a PromiseReaction record. We create a 842 // single reaction record for fulfillment and rejection, therefore our 843 // PromiseReaction implementation needs two [[Handler]] fields. 844 // 845 // The slot value is either a callable object, an integer constant from 846 // the |PromiseHandler| enum, or null. If the value is null, either the 847 // REACTION_FLAG_DEBUGGER_DUMMY or the 848 // REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag must be set. 849 // 850 // After setting the target state for a PromiseReaction, the slot of the 851 // no longer used handler gets reused to store the argument of the active 852 // handler. 853 OnFulfilled, 854 OnRejectedArg = OnFulfilled, 855 OnRejected, 856 OnFulfilledArg = OnRejected, 857 858 // The functions to resolve or reject the promise. Matches the 859 // [[Capability]].[[Resolve]] and [[Capability]].[[Reject]] fields from 860 // the spec. 861 // 862 // The slot values are either callable objects or null, but the latter 863 // case is only allowed if the promise is either a built-in Promise object 864 // or null. 865 Resolve, 866 Reject, 867 868 // Bitmask of the REACTION_FLAG values. 869 Flags, 870 871 // Additional slot to store extra data for specific reaction record types. 872 // 873 // - When the REACTION_FLAG_ASYNC_FUNCTION flag is set, this slot stores 874 // the (internal) generator object for this promise reaction. 875 // - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot stores 876 // the async generator object for this promise reaction. 877 // - When the REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag is set, this 878 // slot stores the promise to resolve when conceptually "calling" the 879 // OnFulfilled or OnRejected handlers. 880 // - When the REACTION_FLAG_ASYNC_FROM_SYNC_ITERATOR is set, this slot 881 // stores 882 // the async-from-sync iterator object. 883 GeneratorOrPromiseToResolveOrAsyncFromSyncIterator, 884 885 SlotCount, 886 }; 887 888 private: 889 template <typename KnownF, typename UnknownF> 890 static void forEachReactionFlag(uint32_t flags, KnownF known, 891 UnknownF unknown); 892 893 void setFlagOnInitialState(uint32_t flag) { 894 int32_t flags = this->flags(); 895 MOZ_ASSERT(flags == 0, "Can't modify with non-default flags"); 896 flags |= flag; 897 setFixedSlot(Slots::Flags, Int32Value(flags)); 898 } 899 900 uint32_t handlerSlot() { 901 MOZ_ASSERT(targetState() != JS::PromiseState::Pending); 902 return targetState() == JS::PromiseState::Fulfilled ? Slots::OnFulfilled 903 : Slots::OnRejected; 904 } 905 906 uint32_t handlerArgSlot() { 907 MOZ_ASSERT(targetState() != JS::PromiseState::Pending); 908 return targetState() == JS::PromiseState::Fulfilled ? Slots::OnFulfilledArg 909 : Slots::OnRejectedArg; 910 } 911 912 public: 913 static const JSClass class_; 914 915 int32_t flags() const { return getFixedSlot(Slots::Flags).toInt32(); } 916 JS::PromiseState targetState() const { 917 int32_t flags = this->flags(); 918 if (!(flags & REACTION_FLAG_RESOLVED)) { 919 return JS::PromiseState::Pending; 920 } 921 return flags & REACTION_FLAG_FULFILLED ? JS::PromiseState::Fulfilled 922 : JS::PromiseState::Rejected; 923 } 924 void setTargetStateAndHandlerArg(JS::PromiseState state, const Value& arg) { 925 MOZ_ASSERT(targetState() == JS::PromiseState::Pending); 926 MOZ_ASSERT(state != JS::PromiseState::Pending, 927 "Can't revert a reaction to pending."); 928 929 int32_t flags = this->flags(); 930 flags |= REACTION_FLAG_RESOLVED; 931 if (state == JS::PromiseState::Fulfilled) { 932 flags |= REACTION_FLAG_FULFILLED; 933 } 934 935 setFixedSlot(Slots::Flags, Int32Value(flags)); 936 setFixedSlot(handlerArgSlot(), arg); 937 } 938 939 void setShouldIgnoreUnhandledRejection() { 940 setFlagOnInitialState(REACTION_FLAG_IGNORE_UNHANDLED_REJECTION); 941 } 942 UnhandledRejectionBehavior unhandledRejectionBehavior() const { 943 int32_t flags = this->flags(); 944 return (flags & REACTION_FLAG_IGNORE_UNHANDLED_REJECTION) 945 ? UnhandledRejectionBehavior::Ignore 946 : UnhandledRejectionBehavior::Report; 947 } 948 949 void setIsDefaultResolvingHandler(PromiseObject* promiseToResolve) { 950 setFlagOnInitialState(REACTION_FLAG_DEFAULT_RESOLVING_HANDLER); 951 setFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator, 952 ObjectValue(*promiseToResolve)); 953 } 954 bool isDefaultResolvingHandler() const { 955 int32_t flags = this->flags(); 956 return flags & REACTION_FLAG_DEFAULT_RESOLVING_HANDLER; 957 } 958 PromiseObject* defaultResolvingPromise() { 959 MOZ_ASSERT(isDefaultResolvingHandler()); 960 const Value& promiseToResolve = 961 getFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator); 962 return &promiseToResolve.toObject().as<PromiseObject>(); 963 } 964 965 void setIsAsyncFunction(AsyncFunctionGeneratorObject* genObj) { 966 MOZ_ASSERT(realm() == genObj->nonCCWRealm()); 967 setFlagOnInitialState(REACTION_FLAG_ASYNC_FUNCTION); 968 setFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator, 969 ObjectValue(*genObj)); 970 } 971 bool isAsyncFunction() const { 972 int32_t flags = this->flags(); 973 return flags & REACTION_FLAG_ASYNC_FUNCTION; 974 } 975 AsyncFunctionGeneratorObject* asyncFunctionGenerator() { 976 MOZ_ASSERT(isAsyncFunction()); 977 const Value& generator = 978 getFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator); 979 AsyncFunctionGeneratorObject* res = 980 &generator.toObject().as<AsyncFunctionGeneratorObject>(); 981 MOZ_RELEASE_ASSERT(realm() == res->realm()); 982 return res; 983 } 984 985 void setIsAsyncGenerator(AsyncGeneratorObject* generator) { 986 setFlagOnInitialState(REACTION_FLAG_ASYNC_GENERATOR); 987 setFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator, 988 ObjectValue(*generator)); 989 } 990 bool isAsyncGenerator() const { 991 int32_t flags = this->flags(); 992 return flags & REACTION_FLAG_ASYNC_GENERATOR; 993 } 994 AsyncGeneratorObject* asyncGenerator() { 995 MOZ_ASSERT(isAsyncGenerator()); 996 const Value& generator = 997 getFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator); 998 return &generator.toObject().as<AsyncGeneratorObject>(); 999 } 1000 1001 void setIsAsyncFromSyncIterator(AsyncFromSyncIteratorObject* iterator) { 1002 setFlagOnInitialState(REACTION_FLAG_ASYNC_FROM_SYNC_ITERATOR); 1003 setFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator, 1004 ObjectValue(*iterator)); 1005 } 1006 bool isAsyncFromSyncIterator() const { 1007 int32_t flags = this->flags(); 1008 return flags & REACTION_FLAG_ASYNC_FROM_SYNC_ITERATOR; 1009 } 1010 AsyncFromSyncIteratorObject* asyncFromSyncIterator() { 1011 MOZ_ASSERT(isAsyncFromSyncIterator()); 1012 const Value& iterator = 1013 getFixedSlot(Slots::GeneratorOrPromiseToResolveOrAsyncFromSyncIterator); 1014 return &iterator.toObject().as<AsyncFromSyncIteratorObject>(); 1015 } 1016 1017 void setIsDebuggerDummy() { 1018 setFlagOnInitialState(REACTION_FLAG_DEBUGGER_DUMMY); 1019 } 1020 bool isDebuggerDummy() const { 1021 int32_t flags = this->flags(); 1022 return flags & REACTION_FLAG_DEBUGGER_DUMMY; 1023 } 1024 1025 Value handler() { 1026 MOZ_ASSERT(targetState() != JS::PromiseState::Pending); 1027 return getFixedSlot(handlerSlot()); 1028 } 1029 Value handlerArg() { 1030 MOZ_ASSERT(targetState() != JS::PromiseState::Pending); 1031 return getFixedSlot(handlerArgSlot()); 1032 } 1033 1034 JSObject* getAndClearHostDefinedData() { 1035 JSObject* obj = getFixedSlot(Slots::HostDefinedData).toObjectOrNull(); 1036 setFixedSlot(Slots::HostDefinedData, UndefinedValue()); 1037 return obj; 1038 } 1039 1040 JSObject* enqueueGlobalRepresentative() const { 1041 return getFixedSlot(Slots::EnqueueGlobalRepresentative).toObjectOrNull(); 1042 } 1043 void setEnqueueGlobalRepresentative(JSObject* obj) { 1044 setFixedSlot(Slots::EnqueueGlobalRepresentative, ObjectOrNullValue(obj)); 1045 } 1046 1047 #if defined(DEBUG) || defined(JS_JITSPEW) 1048 void dumpOwnFields(js::JSONPrinter& json) const; 1049 #endif 1050 }; 1051 1052 const JSClass PromiseReactionRecord::class_ = { 1053 "PromiseReactionRecord", 1054 JSCLASS_HAS_RESERVED_SLOTS(Slots::SlotCount), 1055 }; 1056 1057 class ThenableJob : public MicroTaskEntry { 1058 protected: 1059 enum Slots { 1060 // These slots come directoy after the MicroTaskEntry slots. 1061 Thenable = MicroTaskEntry::Slots::SlotCount, 1062 Then, 1063 Callback, 1064 SlotCount 1065 }; 1066 1067 public: 1068 static const JSClass class_; 1069 1070 enum TargetFunction : int32_t { 1071 PromiseResolveThenableJob, 1072 PromiseResolveBuiltinThenableJob 1073 }; 1074 1075 Value thenable() const { return getFixedSlot(Slots::Thenable); } 1076 1077 void setThenable(const Value& val) { setFixedSlot(Slots::Thenable, val); } 1078 1079 JSObject* then() const { return getFixedSlot(Slots::Then).toObjectOrNull(); } 1080 1081 void setThen(JSObject* obj) { 1082 setFixedSlot(Slots::Then, ObjectOrNullValue(obj)); 1083 } 1084 1085 TargetFunction targetFunction() const { 1086 return static_cast<TargetFunction>(getFixedSlot(Slots::Callback).toInt32()); 1087 } 1088 void setTargetFunction(TargetFunction target) { 1089 setFixedSlot(Slots::Callback, JS::Int32Value(static_cast<int32_t>(target))); 1090 } 1091 }; 1092 1093 const JSClass ThenableJob::class_ = { 1094 "ThenableJob", 1095 JSCLASS_HAS_RESERVED_SLOTS(ThenableJob::SlotCount), 1096 }; 1097 1098 ThenableJob* NewThenableJob(JSContext* cx, ThenableJob::TargetFunction target, 1099 HandleObject promise, HandleValue thenable, 1100 HandleObject then, HandleObject hostDefinedData) { 1101 // MG:XXX: Boy isn't it silly that we have to root here, only to get the 1102 // allocation site... 1103 RootedObject stack( 1104 cx, JS::MaybeGetPromiseAllocationSiteFromPossiblyWrappedPromise(promise)); 1105 if (!cx->compartment()->wrap(cx, &stack)) { 1106 return nullptr; 1107 } 1108 1109 // MG:XXX: Wrapping needs to be delegated to callers I think. 1110 RootedObject hostDefined(cx, hostDefinedData); 1111 if (!cx->compartment()->wrap(cx, &hostDefined)) { 1112 return nullptr; 1113 } 1114 auto* job = NewBuiltinClassInstance<ThenableJob>(cx); 1115 if (!job) { 1116 return nullptr; 1117 } 1118 job->setPromise(promise); 1119 job->setThen(then); 1120 job->setThenable(thenable); 1121 job->setTargetFunction(target); 1122 job->setHostDefinedData(ObjectOrNullValue(hostDefined)); 1123 job->setAllocationStack(stack); 1124 1125 return job; 1126 } 1127 1128 static void AddPromiseFlags(PromiseObject& promise, int32_t flag) { 1129 int32_t flags = promise.flags(); 1130 promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags | flag)); 1131 } 1132 1133 static void RemovePromiseFlags(PromiseObject& promise, int32_t flag) { 1134 int32_t flags = promise.flags(); 1135 promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags & ~flag)); 1136 } 1137 1138 static bool PromiseHasAnyFlag(PromiseObject& promise, int32_t flag) { 1139 return promise.flags() & flag; 1140 } 1141 1142 static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp); 1143 static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp); 1144 1145 static JSFunction* GetResolveFunctionFromReject(JSFunction* reject); 1146 static JSFunction* GetRejectFunctionFromResolve(JSFunction* resolve); 1147 1148 #ifdef DEBUG 1149 1150 /** 1151 * Returns Promise Resolve Function's [[AlreadyResolved]].[[Value]]. 1152 */ 1153 static bool IsAlreadyResolvedResolveFunction(JSFunction* resolveFun) { 1154 MOZ_ASSERT(resolveFun->maybeNative() == ResolvePromiseFunction); 1155 1156 bool alreadyResolved = 1157 resolveFun->getExtendedSlot(ResolveFunctionSlot_Promise).isUndefined(); 1158 1159 // Other slots should agree. 1160 if (alreadyResolved) { 1161 MOZ_ASSERT(resolveFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction) 1162 .isUndefined()); 1163 } else { 1164 JSFunction* rejectFun = GetRejectFunctionFromResolve(resolveFun); 1165 MOZ_ASSERT( 1166 !rejectFun->getExtendedSlot(RejectFunctionSlot_Promise).isUndefined()); 1167 MOZ_ASSERT(!rejectFun->getExtendedSlot(RejectFunctionSlot_ResolveFunction) 1168 .isUndefined()); 1169 } 1170 1171 return alreadyResolved; 1172 } 1173 1174 /** 1175 * Returns Promise Reject Function's [[AlreadyResolved]].[[Value]]. 1176 */ 1177 static bool IsAlreadyResolvedRejectFunction(JSFunction* rejectFun) { 1178 MOZ_ASSERT(rejectFun->maybeNative() == RejectPromiseFunction); 1179 1180 bool alreadyResolved = 1181 rejectFun->getExtendedSlot(RejectFunctionSlot_Promise).isUndefined(); 1182 1183 // Other slots should agree. 1184 if (alreadyResolved) { 1185 MOZ_ASSERT(rejectFun->getExtendedSlot(RejectFunctionSlot_ResolveFunction) 1186 .isUndefined()); 1187 } else { 1188 JSFunction* resolveFun = GetResolveFunctionFromReject(rejectFun); 1189 MOZ_ASSERT(!resolveFun->getExtendedSlot(ResolveFunctionSlot_Promise) 1190 .isUndefined()); 1191 MOZ_ASSERT(!resolveFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction) 1192 .isUndefined()); 1193 } 1194 1195 return alreadyResolved; 1196 } 1197 1198 #endif // DEBUG 1199 1200 /** 1201 * Set Promise Resolve Function's and Promise Reject Function's 1202 * [[AlreadyResolved]].[[Value]] to true. 1203 * 1204 * `resolutionFun` can be either of them. 1205 */ 1206 static void SetAlreadyResolvedResolutionFunction(JSFunction* resolutionFun) { 1207 JSFunction* resolve; 1208 JSFunction* reject; 1209 if (resolutionFun->maybeNative() == ResolvePromiseFunction) { 1210 resolve = resolutionFun; 1211 reject = GetRejectFunctionFromResolve(resolutionFun); 1212 } else { 1213 resolve = GetResolveFunctionFromReject(resolutionFun); 1214 reject = resolutionFun; 1215 } 1216 1217 resolve->setExtendedSlot(ResolveFunctionSlot_Promise, UndefinedValue()); 1218 resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, 1219 UndefinedValue()); 1220 1221 reject->setExtendedSlot(RejectFunctionSlot_Promise, UndefinedValue()); 1222 reject->setExtendedSlot(RejectFunctionSlot_ResolveFunction, UndefinedValue()); 1223 1224 MOZ_ASSERT(IsAlreadyResolvedResolveFunction(resolve)); 1225 MOZ_ASSERT(IsAlreadyResolvedRejectFunction(reject)); 1226 } 1227 1228 /** 1229 * Returns true if given promise is created by 1230 * CreatePromiseObjectWithoutResolutionFunctions. 1231 */ 1232 bool js::IsPromiseWithDefaultResolvingFunction(PromiseObject* promise) { 1233 return PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS); 1234 } 1235 1236 /** 1237 * Returns Promise Resolve Function's [[AlreadyResolved]].[[Value]] for 1238 * a promise created by CreatePromiseObjectWithoutResolutionFunctions. 1239 */ 1240 static bool IsAlreadyResolvedPromiseWithDefaultResolvingFunction( 1241 PromiseObject* promise) { 1242 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 1243 1244 if (promise->as<PromiseObject>().state() != JS::PromiseState::Pending) { 1245 MOZ_ASSERT(PromiseHasAnyFlag( 1246 *promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS_ALREADY_RESOLVED)); 1247 return true; 1248 } 1249 1250 return PromiseHasAnyFlag( 1251 *promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS_ALREADY_RESOLVED); 1252 } 1253 1254 /** 1255 * Set Promise Resolve Function's [[AlreadyResolved]].[[Value]] to true for 1256 * a promise created by CreatePromiseObjectWithoutResolutionFunctions. 1257 */ 1258 void js::SetAlreadyResolvedPromiseWithDefaultResolvingFunction( 1259 PromiseObject* promise) { 1260 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 1261 1262 promise->setFixedSlot( 1263 PromiseSlot_Flags, 1264 JS::Int32Value( 1265 promise->flags() | 1266 PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS_ALREADY_RESOLVED)); 1267 } 1268 1269 /** 1270 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1271 * 1272 * CreateResolvingFunctions ( promise ) 1273 * https://tc39.es/ecma262/#sec-createresolvingfunctions 1274 */ 1275 [[nodiscard]] static MOZ_ALWAYS_INLINE bool CreateResolvingFunctions( 1276 JSContext* cx, HandleObject promise, MutableHandleObject resolveFn, 1277 MutableHandleObject rejectFn) { 1278 // Step 1. Let alreadyResolved be the Record { [[Value]]: false }. 1279 // (implicit, see steps 5-6, 10-11 below) 1280 1281 // Step 2. Let stepsResolve be the algorithm steps defined in Promise Resolve 1282 // Functions. 1283 // Step 3. Let lengthResolve be the number of non-optional parameters of the 1284 // function definition in Promise Resolve Functions. 1285 // Step 4. Let resolve be 1286 // ! CreateBuiltinFunction(stepsResolve, lengthResolve, "", 1287 // « [[Promise]], [[AlreadyResolved]] »). 1288 Handle<PropertyName*> funName = cx->names().empty_; 1289 resolveFn.set(NewNativeFunction(cx, ResolvePromiseFunction, 1, funName, 1290 gc::AllocKind::FUNCTION_EXTENDED, 1291 GenericObject)); 1292 if (!resolveFn) { 1293 return false; 1294 } 1295 1296 // Step 7. Let stepsReject be the algorithm steps defined in Promise Reject 1297 // Functions. 1298 // Step 8. Let lengthReject be the number of non-optional parameters of the 1299 // function definition in Promise Reject Functions. 1300 // Step 9. Let reject be 1301 // ! CreateBuiltinFunction(stepsReject, lengthReject, "", 1302 // « [[Promise]], [[AlreadyResolved]] »). 1303 rejectFn.set(NewNativeFunction(cx, RejectPromiseFunction, 1, funName, 1304 gc::AllocKind::FUNCTION_EXTENDED, 1305 GenericObject)); 1306 if (!rejectFn) { 1307 return false; 1308 } 1309 1310 JSFunction* resolveFun = &resolveFn->as<JSFunction>(); 1311 JSFunction* rejectFun = &rejectFn->as<JSFunction>(); 1312 1313 // Step 5. Set resolve.[[Promise]] to promise. 1314 // Step 6. Set resolve.[[AlreadyResolved]] to alreadyResolved. 1315 // 1316 // NOTE: We use these references as [[AlreadyResolved]].[[Value]]. 1317 // See the comment in ResolveFunctionSlots for more details. 1318 resolveFun->initExtendedSlot(ResolveFunctionSlot_Promise, 1319 ObjectValue(*promise)); 1320 resolveFun->initExtendedSlot(ResolveFunctionSlot_RejectFunction, 1321 ObjectValue(*rejectFun)); 1322 1323 // Step 10. Set reject.[[Promise]] to promise. 1324 // Step 11. Set reject.[[AlreadyResolved]] to alreadyResolved. 1325 // 1326 // NOTE: We use these references as [[AlreadyResolved]].[[Value]]. 1327 // See the comment in ResolveFunctionSlots for more details. 1328 rejectFun->initExtendedSlot(RejectFunctionSlot_Promise, 1329 ObjectValue(*promise)); 1330 rejectFun->initExtendedSlot(RejectFunctionSlot_ResolveFunction, 1331 ObjectValue(*resolveFun)); 1332 1333 MOZ_ASSERT(!IsAlreadyResolvedResolveFunction(resolveFun)); 1334 MOZ_ASSERT(!IsAlreadyResolvedRejectFunction(rejectFun)); 1335 1336 // Step 12. Return the Record { [[Resolve]]: resolve, [[Reject]]: reject }. 1337 return true; 1338 } 1339 1340 static bool IsSettledMaybeWrappedPromise(JSObject* promise) { 1341 if (IsProxy(promise)) { 1342 promise = UncheckedUnwrap(promise); 1343 1344 // Caller needs to handle dead wrappers. 1345 if (JS_IsDeadWrapper(promise)) { 1346 return false; 1347 } 1348 } 1349 1350 return promise->as<PromiseObject>().state() != JS::PromiseState::Pending; 1351 } 1352 1353 [[nodiscard]] static bool RejectMaybeWrappedPromise( 1354 JSContext* cx, HandleObject promiseObj, HandleValue reason, 1355 Handle<SavedFrame*> unwrappedRejectionStack); 1356 1357 /** 1358 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1359 * 1360 * Promise Reject Functions 1361 * https://tc39.es/ecma262/#sec-promise-reject-functions 1362 */ 1363 static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp) { 1364 CallArgs args = CallArgsFromVp(argc, vp); 1365 1366 JSFunction* reject = &args.callee().as<JSFunction>(); 1367 HandleValue reasonVal = args.get(0); 1368 1369 // Step 1. Let F be the active function object. 1370 // Step 2. Assert: F has a [[Promise]] internal slot whose value is an Object. 1371 // (implicit) 1372 1373 // Step 3. Let promise be F.[[Promise]]. 1374 const Value& promiseVal = reject->getExtendedSlot(RejectFunctionSlot_Promise); 1375 1376 // Step 4. Let alreadyResolved be F.[[AlreadyResolved]]. 1377 // Step 5. If alreadyResolved.[[Value]] is true, return undefined. 1378 // 1379 // If the Promise isn't available anymore, it has been resolved and the 1380 // reference to it removed to make it eligible for collection. 1381 bool alreadyResolved = promiseVal.isUndefined(); 1382 MOZ_ASSERT(IsAlreadyResolvedRejectFunction(reject) == alreadyResolved); 1383 if (alreadyResolved) { 1384 args.rval().setUndefined(); 1385 return true; 1386 } 1387 1388 RootedObject promise(cx, &promiseVal.toObject()); 1389 1390 // Step 6. Set alreadyResolved.[[Value]] to true. 1391 SetAlreadyResolvedResolutionFunction(reject); 1392 1393 // In some cases the Promise reference on the resolution function won't 1394 // have been removed during resolution, so we need to check that here, 1395 // too. 1396 if (IsSettledMaybeWrappedPromise(promise)) { 1397 args.rval().setUndefined(); 1398 return true; 1399 } 1400 1401 // Step 7. Return RejectPromise(promise, reason). 1402 if (!RejectMaybeWrappedPromise(cx, promise, reasonVal, nullptr)) { 1403 return false; 1404 } 1405 args.rval().setUndefined(); 1406 return true; 1407 } 1408 1409 [[nodiscard]] static bool FulfillMaybeWrappedPromise(JSContext* cx, 1410 HandleObject promiseObj, 1411 HandleValue value_); 1412 1413 [[nodiscard]] static bool EnqueuePromiseResolveThenableJob( 1414 JSContext* cx, HandleValue promiseToResolve, HandleValue thenable, 1415 HandleValue thenVal); 1416 1417 [[nodiscard]] static bool EnqueuePromiseResolveThenableBuiltinJob( 1418 JSContext* cx, HandleObject promiseToResolve, HandleObject thenable); 1419 1420 static bool Promise_then_impl(JSContext* cx, HandleValue promiseVal, 1421 HandleValue onFulfilled, HandleValue onRejected, 1422 MutableHandleValue rval, bool rvalExplicitlyUsed); 1423 1424 // This is used to get the 'then' property off of an object, and report some 1425 // information back for telemetry purposes. When we no longer need this 1426 // telemetry this function can be removed and replaced with GetProperty 1427 bool GetThenValue(JSContext* cx, JS::Handle<JSObject*> obj, 1428 JS::Handle<JS::Value> reciever, 1429 JS::MutableHandle<Value> thenVal, bool* isOnProto, 1430 bool* isOnStandardProto, bool* isOnObjectProto) { 1431 MOZ_ASSERT(isOnProto && *isOnProto == false); 1432 MOZ_ASSERT(isOnStandardProto && *isOnStandardProto == false); 1433 MOZ_ASSERT(isOnObjectProto && *isOnObjectProto == false); 1434 1435 NativeObject* holder; 1436 PropertyResult prop; 1437 1438 // LookupProperty would be observable unforunately. If we can do the lookup, 1439 // then we can produce information, but otherwise we're left blind. 1440 // Fortunately, since this is purely for the purposes of telemetry, let's just 1441 // use Pure. 1442 RootedId thenId(cx, NameToId(cx->names().then)); 1443 1444 // If we're doing the lookup on the original promise prototype we want to only 1445 // report telemetry if the value is not the original Promise.prototype.then 1446 // 1447 // We then need to defer until after the lookup to decide this. 1448 bool maybeOnPromiseProto = false; 1449 do { 1450 if (LookupPropertyPure(cx, obj, thenId, &holder, &prop)) { 1451 if (prop.isNotFound()) { 1452 break; 1453 } 1454 1455 if (holder != obj) { 1456 *isOnProto = true; 1457 1458 auto key = JS::IdentifyStandardPrototype(holder); 1459 if (key != JSProto_Null) { 1460 if (key == JSProto_Promise) { 1461 maybeOnPromiseProto = true; 1462 } else { 1463 *isOnStandardProto = true; 1464 if (key == JSProto_Object) { 1465 *isOnObjectProto = true; 1466 } 1467 } 1468 } 1469 } 1470 } 1471 } while (false); 1472 1473 if (!GetProperty(cx, obj, reciever, cx->names().then, thenVal)) { 1474 return false; 1475 } 1476 1477 if (maybeOnPromiseProto) { 1478 *isOnStandardProto = !IsNativeFunction(thenVal, Promise_then); 1479 } 1480 1481 return true; 1482 } 1483 1484 void ReportThenable(JSContext* cx, bool isOnProto, bool isOnStandardProto, 1485 bool isOnObjectProto) { 1486 cx->runtime()->setUseCounter(cx->global(), JSUseCounter::THENABLE_USE); 1487 1488 if (isOnProto) { 1489 cx->runtime()->setUseCounter(cx->global(), 1490 JSUseCounter::THENABLE_USE_PROTO); 1491 JS_LOG(thenable, Debug, "Thenable on proto"); 1492 } 1493 1494 if (isOnStandardProto) { 1495 cx->runtime()->setUseCounter(cx->global(), 1496 JSUseCounter::THENABLE_USE_STANDARD_PROTO); 1497 JS_LOG(thenable, Info, "Thenable on standard proto"); 1498 } 1499 1500 if (isOnObjectProto) { 1501 cx->runtime()->setUseCounter(cx->global(), 1502 JSUseCounter::THENABLE_USE_OBJECT_PROTO); 1503 JS_LOG(thenable, Info, "Thenable on Object.prototype"); 1504 } 1505 } 1506 1507 /** 1508 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1509 * 1510 * Promise Resolve Functions 1511 * https://tc39.es/ecma262/#sec-promise-resolve-functions 1512 * 1513 * Steps 7-15. 1514 */ 1515 [[nodiscard]] bool js::ResolvePromiseInternal( 1516 JSContext* cx, JS::Handle<JSObject*> promise, 1517 JS::Handle<JS::Value> resolutionVal) { 1518 cx->check(promise, resolutionVal); 1519 MOZ_ASSERT(!IsSettledMaybeWrappedPromise(promise)); 1520 1521 // (reordered) 1522 // Step 8. If Type(resolution) is not Object, then 1523 if (!resolutionVal.isObject()) { 1524 // Step 8.a. Return FulfillPromise(promise, resolution). 1525 return FulfillMaybeWrappedPromise(cx, promise, resolutionVal); 1526 } 1527 1528 RootedObject resolution(cx, &resolutionVal.toObject()); 1529 1530 // Step 7. If SameValue(resolution, promise) is true, then 1531 if (resolution == promise) { 1532 // Step 7.a. Let selfResolutionError be a newly created TypeError object. 1533 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1534 JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF); 1535 RootedValue selfResolutionError(cx); 1536 Rooted<SavedFrame*> stack(cx); 1537 if (!MaybeGetAndClearExceptionAndStack(cx, &selfResolutionError, &stack)) { 1538 return false; 1539 } 1540 1541 // Step 7.b. Return RejectPromise(promise, selfResolutionError). 1542 return RejectMaybeWrappedPromise(cx, promise, selfResolutionError, stack); 1543 } 1544 1545 // Step 9. Let then be Get(resolution, "then"). 1546 RootedValue thenVal(cx); 1547 bool isOnProto = false; 1548 bool isOnStandardProto = false; 1549 bool isOnObjectProto = false; 1550 bool status = GetThenValue(cx, resolution, resolutionVal, &thenVal, 1551 &isOnProto, &isOnStandardProto, &isOnObjectProto); 1552 1553 RootedValue error(cx); 1554 Rooted<SavedFrame*> errorStack(cx); 1555 1556 // Step 10. If then is an abrupt completion, then 1557 if (!status) { 1558 // Get the `then.[[Value]]` value used in the step 10.a. 1559 if (!MaybeGetAndClearExceptionAndStack(cx, &error, &errorStack)) { 1560 return false; 1561 } 1562 } 1563 1564 // Testing functions allow to directly settle a promise without going 1565 // through the resolving functions. In that case the normal bookkeeping to 1566 // ensure only pending promises can be resolved doesn't apply and we need 1567 // to manually check for already settled promises. The exception is simply 1568 // dropped when this case happens. 1569 if (IsSettledMaybeWrappedPromise(promise)) { 1570 return true; 1571 } 1572 1573 // Step 10. If then is an abrupt completion, then 1574 if (!status) { 1575 // Step 10.a. Return RejectPromise(promise, then.[[Value]]). 1576 return RejectMaybeWrappedPromise(cx, promise, error, errorStack); 1577 } 1578 1579 // Step 11. Let thenAction be then.[[Value]]. 1580 // (implicit) 1581 1582 // Step 12. If IsCallable(thenAction) is false, then 1583 if (!IsCallable(thenVal)) { 1584 // Step 12.a. Return FulfillPromise(promise, resolution). 1585 return FulfillMaybeWrappedPromise(cx, promise, resolutionVal); 1586 } 1587 1588 // Step 13. Let thenJobCallback be HostMakeJobCallback(thenAction). 1589 // (implicit) 1590 1591 // Step 14. Let job be 1592 // NewPromiseResolveThenableJob(promise, resolution, 1593 // thenJobCallback). 1594 // Step 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 1595 1596 // If the resolution object is a built-in Promise object and the 1597 // `then` property is the original Promise.prototype.then function 1598 // from the current realm, we skip storing/calling it. 1599 // Additionally we require that |promise| itself is also a built-in 1600 // Promise object, so the fast path doesn't need to cope with wrappers. 1601 bool isBuiltinThen = false; 1602 if (resolution->is<PromiseObject>() && promise->is<PromiseObject>() && 1603 IsNativeFunction(thenVal, Promise_then) && 1604 thenVal.toObject().as<JSFunction>().realm() == cx->realm()) { 1605 isBuiltinThen = true; 1606 } 1607 1608 if (!isBuiltinThen) { 1609 ReportThenable(cx, isOnProto, isOnStandardProto, isOnObjectProto); 1610 1611 RootedValue promiseVal(cx, ObjectValue(*promise)); 1612 if (!EnqueuePromiseResolveThenableJob(cx, promiseVal, resolutionVal, 1613 thenVal)) { 1614 return false; 1615 } 1616 } else { 1617 if (!EnqueuePromiseResolveThenableBuiltinJob(cx, promise, resolution)) { 1618 return false; 1619 } 1620 } 1621 1622 return true; 1623 } 1624 1625 /** 1626 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1627 * 1628 * Promise Resolve Functions 1629 * https://tc39.es/ecma262/#sec-promise-resolve-functions 1630 */ 1631 static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) { 1632 CallArgs args = CallArgsFromVp(argc, vp); 1633 1634 // Step 1. Let F be the active function object. 1635 // Step 2. Assert: F has a [[Promise]] internal slot whose value is an Object. 1636 // (implicit) 1637 1638 JSFunction* resolve = &args.callee().as<JSFunction>(); 1639 HandleValue resolutionVal = args.get(0); 1640 1641 // Step 3. Let promise be F.[[Promise]]. 1642 const Value& promiseVal = 1643 resolve->getExtendedSlot(ResolveFunctionSlot_Promise); 1644 1645 // Step 4. Let alreadyResolved be F.[[AlreadyResolved]]. 1646 // Step 5. If alreadyResolved.[[Value]] is true, return undefined. 1647 // 1648 // NOTE: We use the reference to the reject function as [[AlreadyResolved]]. 1649 bool alreadyResolved = promiseVal.isUndefined(); 1650 MOZ_ASSERT(IsAlreadyResolvedResolveFunction(resolve) == alreadyResolved); 1651 if (alreadyResolved) { 1652 args.rval().setUndefined(); 1653 return true; 1654 } 1655 1656 RootedObject promise(cx, &promiseVal.toObject()); 1657 1658 // Step 6. Set alreadyResolved.[[Value]] to true. 1659 SetAlreadyResolvedResolutionFunction(resolve); 1660 1661 // In some cases the Promise reference on the resolution function won't 1662 // have been removed during resolution, so we need to check that here, 1663 // too. 1664 if (IsSettledMaybeWrappedPromise(promise)) { 1665 args.rval().setUndefined(); 1666 return true; 1667 } 1668 1669 // Steps 7-15. 1670 if (!ResolvePromiseInternal(cx, promise, resolutionVal)) { 1671 return false; 1672 } 1673 1674 // Step 16. Return undefined. 1675 args.rval().setUndefined(); 1676 return true; 1677 } 1678 1679 static bool EnqueueJob(JSContext* cx, JS::JSMicroTask* job) { 1680 MOZ_ASSERT(cx->realm()); 1681 GeckoProfilerRuntime& profiler = cx->runtime()->geckoProfiler(); 1682 if (profiler.enabled()) { 1683 // Emit a flow start marker here. 1684 uint64_t uid = 0; 1685 if (JS::GetFlowIdFromJSMicroTask(job, &uid)) { 1686 profiler.markFlow("JS::EnqueueJob", uid, 1687 JS::ProfilingCategoryPair::OTHER); 1688 } 1689 } 1690 1691 // We need to root this job because useDebugQueue can GC. 1692 Rooted<JS::JSMicroTask*> rootedJob(cx, job); 1693 1694 // Only check if we need to use the debug queue when we're not on main thread. 1695 if (MOZ_UNLIKELY(!cx->runtime()->isMainRuntime() && 1696 cx->jobQueue->useDebugQueue(cx->global()))) { 1697 return cx->microTaskQueues->enqueueDebugMicroTask(cx, 1698 ObjectValue(*rootedJob)); 1699 } 1700 return cx->microTaskQueues->enqueueRegularMicroTask(cx, 1701 ObjectValue(*rootedJob)); 1702 } 1703 1704 static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp); 1705 1706 /** 1707 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1708 * 1709 * NewPromiseReactionJob ( reaction, argument ) 1710 * https://tc39.es/ecma262/#sec-newpromisereactionjob 1711 * HostEnqueuePromiseJob ( job, realm ) 1712 * https://tc39.es/ecma262/#sec-hostenqueuepromisejob 1713 * 1714 * Tells the embedding to enqueue a Promise reaction job, based on 1715 * three parameters: 1716 * reactionObj - The reaction record. 1717 * handlerArg_ - The first and only argument to pass to the handler invoked by 1718 * the job. This will be stored on the reaction record. 1719 * targetState - The PromiseState this reaction job targets. This decides 1720 * whether the onFulfilled or onRejected handler is called. 1721 */ 1722 [[nodiscard]] static bool EnqueuePromiseReactionJob( 1723 JSContext* cx, HandleObject reactionObj, HandleValue handlerArg_, 1724 JS::PromiseState targetState) { 1725 MOZ_ASSERT(targetState == JS::PromiseState::Fulfilled || 1726 targetState == JS::PromiseState::Rejected); 1727 1728 // The reaction might have been stored on a Promise from another 1729 // compartment, which means it would've been wrapped in a CCW. 1730 // To properly handle that case here, unwrap it and enter its 1731 // compartment, where the job creation should take place anyway. 1732 RootedTuple<PromiseReactionRecord*, Value, Value, Value, JSObject*, 1733 JSFunction*, JSObject*, JSObject*> 1734 roots(cx); 1735 RootedField<PromiseReactionRecord*, 0> reaction(roots); 1736 RootedField<Value, 1> handlerArg(roots, handlerArg_); 1737 mozilla::Maybe<AutoRealm> ar; 1738 if (!IsProxy(reactionObj)) { 1739 MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>()); 1740 reaction = &reactionObj->as<PromiseReactionRecord>(); 1741 if (cx->realm() != reaction->realm()) { 1742 // If the compartment has multiple realms, create the job in the 1743 // reaction's realm. This is consistent with the code in the else-branch 1744 // and avoids problems with running jobs against a dying global (Gecko 1745 // drops such jobs). 1746 ar.emplace(cx, reaction); 1747 } 1748 } else { 1749 JSObject* unwrappedReactionObj = UncheckedUnwrap(reactionObj); 1750 if (JS_IsDeadWrapper(unwrappedReactionObj)) { 1751 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1752 JSMSG_DEAD_OBJECT); 1753 return false; 1754 } 1755 reaction = &unwrappedReactionObj->as<PromiseReactionRecord>(); 1756 MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>()); 1757 ar.emplace(cx, reaction); 1758 if (!cx->compartment()->wrap(cx, &handlerArg)) { 1759 return false; 1760 } 1761 } 1762 1763 // Must not enqueue a reaction job more than once. 1764 MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending); 1765 1766 // NOTE: Instead of capturing reaction and arguments separately in the 1767 // Job Abstract Closure below, store arguments (= handlerArg) in 1768 // reaction object and capture it. 1769 // Also, set reaction.[[Type]] is represented by targetState here. 1770 cx->check(handlerArg); 1771 reaction->setTargetStateAndHandlerArg(targetState, handlerArg); 1772 1773 RootedField<Value, 2> reactionVal(roots, ObjectValue(*reaction)); 1774 RootedField<Value, 3> handler(roots, reaction->handler()); 1775 1776 // NewPromiseReactionJob 1777 // Step 2. Let handlerRealm be null. 1778 // NOTE: Instead of passing job and realm separately, we use the job's 1779 // JSFunction object's realm as the job's realm. 1780 // So we should enter the handlerRealm before creating the job function. 1781 // 1782 // GetFunctionRealm performed inside AutoFunctionOrCurrentRealm uses checked 1783 // unwrap and it can hit permission error if there's a security wrapper, and 1784 // in that case the reaction job is created in the current realm, instead of 1785 // the target function's realm. 1786 // 1787 // If this reaction crosses chrome/content boundary, and the security 1788 // wrapper would allow "call" operation, it still works inside the 1789 // reaction job. 1790 // 1791 // This behavior is observable only when the job belonging to the content 1792 // realm stops working (*1, *2), and it won't matter in practice. 1793 // 1794 // *1: "we can run script" performed inside HostEnqueuePromiseJob 1795 // in HTML spec 1796 // https://html.spec.whatwg.org/#hostenqueuepromisejob 1797 // https://html.spec.whatwg.org/#check-if-we-can-run-script 1798 // https://html.spec.whatwg.org/#fully-active 1799 // *2: nsIGlobalObject::IsDying performed inside PromiseJobRunnable::Run 1800 // in our implementation 1801 mozilla::Maybe<AutoFunctionOrCurrentRealm> ar2; 1802 1803 // NewPromiseReactionJob 1804 // Step 3. If reaction.[[Handler]] is not empty, then 1805 if (handler.isObject()) { 1806 // Step 3.a. Let getHandlerRealmResult be 1807 // GetFunctionRealm(reaction.[[Handler]].[[Callback]]). 1808 // Step 3.b. If getHandlerRealmResult is a normal completion, 1809 // set handlerRealm to getHandlerRealmResult.[[Value]]. 1810 // Step 3.c. Else, set handlerRealm to the current Realm Record. 1811 // Step 3.d. NOTE: handlerRealm is never null unless the handler is 1812 // undefined. When the handler is a revoked Proxy and no 1813 // ECMAScript code runs, handlerRealm is used to create error 1814 // objects. 1815 RootedField<JSObject*, 4> handlerObj(roots, &handler.toObject()); 1816 ar2.emplace(cx, handlerObj); 1817 1818 // This is wrapped here because it may be a cross comaprtment 1819 // reference, and so should be wrapped to be stored on the job function. 1820 // (it's also important because this indicates to PromiseReactionJob 1821 // that it needs to switch realms). 1822 if (!cx->compartment()->wrap(cx, &reactionVal)) { 1823 return false; 1824 } 1825 } 1826 1827 // When using JS::AddPromiseReactions{,IgnoringUnHandledRejection}, no actual 1828 // promise is created, so we might not have one here. 1829 // 1830 // Bug 1977691: This comment needs updating; I don't think 1831 // JS::AddPromiseReactions happens without a promise anymore, _however_ async 1832 // functions may not have a promise. 1833 // 1834 // 1835 // Additionally, we might have an object here that isn't an instance of 1836 // Promise. This can happen if content overrides the value of 1837 // Promise[@@species] (or invokes Promise#then on a Promise subclass 1838 // instance with a non-default @@species value on the constructor) with a 1839 // function that returns objects that're not Promise (subclass) instances. 1840 // In that case, we just pretend we didn't have an object in the first 1841 // place. 1842 // If after all this we do have an object, wrap it in case we entered the 1843 // handler's compartment above, because we should pass objects from a 1844 // single compartment to the enqueuePromiseJob callback. 1845 RootedField<JSObject*, 6> promise(roots, reaction->promise()); 1846 if (promise) { 1847 if (promise->is<PromiseObject>()) { 1848 if (!cx->compartment()->wrap(cx, &promise)) { 1849 return false; 1850 } 1851 } else if (IsWrapper(promise)) { 1852 // `promise` can be already-wrapped promise object at this point. 1853 JSObject* unwrappedPromise = UncheckedUnwrap(promise); 1854 if (unwrappedPromise->is<PromiseObject>()) { 1855 if (!cx->compartment()->wrap(cx, &promise)) { 1856 return false; 1857 } 1858 } else { 1859 promise = nullptr; 1860 } 1861 } else { 1862 promise = nullptr; 1863 } 1864 } 1865 1866 // NewPromiseReactionJob 1867 // Step 1 (reordered). Let job be a new Job Abstract Closure with no 1868 // parameters that captures reaction and argument 1869 // and performs the following steps when called: 1870 if (JS::Prefs::use_js_microtask_queue()) { 1871 MOZ_ASSERT(reactionVal.isObject()); 1872 1873 // Get a representative object for this global: We will use this later 1874 // to extract the target global for execution. We don't store the global 1875 // directly because CCWs to globals can change identity. 1876 // 1877 // So instead we simply store Object.prototype from the target global, 1878 // an object which always exists. 1879 RootedObject globalRepresentative(cx, &cx->global()->getObjectPrototype()); 1880 1881 // PromiseReactionJob job will use the existence of a CCW as a signal 1882 // to change to the reactionVal's realm for execution. I believe 1883 // this is the right thing to do. As a result however we don't actually 1884 // need to track the global. We simply allow PromiseReactionJob to 1885 // do the right thing. We will need to enqueue a CCW however 1886 { 1887 AutoRealm ar(cx, reaction); 1888 1889 RootedObject stack( 1890 cx, 1891 JS::MaybeGetPromiseAllocationSiteFromPossiblyWrappedPromise(promise)); 1892 if (!cx->compartment()->wrap(cx, &stack)) { 1893 return false; 1894 } 1895 reaction->setAllocationStack(stack); 1896 1897 if (!reaction->getHostDefinedData().isObject()) { 1898 // We do need to still provide an incumbentGlobal here 1899 // MG:XXX: I'm pretty sure this can be appreciably more elegant later. 1900 RootedObject hostGlobal(cx); 1901 if (!cx->jobQueue->getHostDefinedGlobal(cx, &hostGlobal)) { 1902 return false; 1903 } 1904 1905 if (hostGlobal) { 1906 MOZ_ASSERT(hostGlobal->is<GlobalObject>()); 1907 // Recycle the root -- we store the prototype for the same 1908 // reason as EnqueueGlobalRepresentative. 1909 hostGlobal = &hostGlobal->as<GlobalObject>().getObjectPrototype(); 1910 } 1911 1912 if (!cx->compartment()->wrap(cx, &hostGlobal)) { 1913 return false; 1914 } 1915 reaction->setHostDefinedGlobalRepresentative(hostGlobal); 1916 } 1917 1918 if (!cx->compartment()->wrap(cx, &globalRepresentative)) { 1919 return false; 1920 } 1921 reaction->setEnqueueGlobalRepresentative(globalRepresentative); 1922 } 1923 1924 if (!cx->compartment()->wrap(cx, &reactionVal)) { 1925 return false; 1926 } 1927 1928 // HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 1929 return EnqueueJob(cx, &reactionVal.toObject()); 1930 } 1931 1932 RootedField<JSObject*, 7> hostDefinedData(roots); 1933 if (JSObject* hostDefined = reaction->getAndClearHostDefinedData()) { 1934 hostDefined = CheckedUnwrapStatic(hostDefined); 1935 MOZ_ASSERT(hostDefined); 1936 // If the hostDefined object becomes a dead wrapper here, the target 1937 // global has already gone, and the job queue won't run the promise job 1938 // anyway. 1939 if (JS_IsDeadWrapper(hostDefined)) { 1940 return true; 1941 } 1942 hostDefinedData = hostDefined; 1943 } 1944 1945 Handle<PropertyName*> funName = cx->names().empty_; 1946 RootedField<JSFunction*, 5> job( 1947 roots, 1948 NewNativeFunction(cx, PromiseReactionJob, 0, funName, 1949 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 1950 if (!job) { 1951 return false; 1952 } 1953 1954 job->setExtendedSlot(ReactionJobSlot_ReactionRecord, reactionVal); 1955 1956 return cx->runtime()->enqueuePromiseJob(cx, job, promise, hostDefinedData); 1957 } 1958 1959 [[nodiscard]] static bool TriggerPromiseReactions(JSContext* cx, 1960 HandleValue reactionsVal, 1961 JS::PromiseState state, 1962 HandleValue valueOrReason); 1963 1964 /** 1965 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 1966 * 1967 * FulfillPromise ( promise, value ) 1968 * https://tc39.es/ecma262/#sec-fulfillpromise 1969 * RejectPromise ( promise, reason ) 1970 * https://tc39.es/ecma262/#sec-rejectpromise 1971 * 1972 * This method takes an additional optional |unwrappedRejectionStack| parameter, 1973 * which is only used for debugging purposes. 1974 * It allows callers to to pass in the stack of some exception which 1975 * triggered the rejection of the promise. 1976 */ 1977 [[nodiscard]] static bool ResolvePromise( 1978 JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueOrReason, 1979 JS::PromiseState state, 1980 Handle<SavedFrame*> unwrappedRejectionStack = nullptr) { 1981 // Step 1. Assert: The value of promise.[[PromiseState]] is pending. 1982 MOZ_ASSERT(promise->state() == JS::PromiseState::Pending); 1983 MOZ_ASSERT(state == JS::PromiseState::Fulfilled || 1984 state == JS::PromiseState::Rejected); 1985 MOZ_ASSERT_IF(unwrappedRejectionStack, state == JS::PromiseState::Rejected); 1986 1987 // FulfillPromise 1988 // Step 2. Let reactions be promise.[[PromiseFulfillReactions]]. 1989 // RejectPromise 1990 // Step 2. Let reactions be promise.[[PromiseRejectReactions]]. 1991 // 1992 // We only have one list of reactions for both resolution types. So 1993 // instead of getting the right list of reactions, we determine the 1994 // resolution type to retrieve the right information from the 1995 // reaction records. 1996 RootedValue reactionsVal(cx, promise->reactions()); 1997 1998 // FulfillPromise 1999 // Step 3. Set promise.[[PromiseResult]] to value. 2000 // RejectPromise 2001 // Step 3. Set promise.[[PromiseResult]] to reason. 2002 // 2003 // Step 4. Set promise.[[PromiseFulfillReactions]] to undefined. 2004 // Step 5. Set promise.[[PromiseRejectReactions]] to undefined. 2005 // 2006 // The same slot is used for the reactions list and the result, so setting 2007 // the result also removes the reactions list. 2008 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, valueOrReason); 2009 2010 // FulfillPromise 2011 // Step 6. Set promise.[[PromiseState]] to fulfilled. 2012 // RejectPromise 2013 // Step 6. Set promise.[[PromiseState]] to rejected. 2014 int32_t flags = promise->flags(); 2015 flags |= PROMISE_FLAG_RESOLVED; 2016 if (state == JS::PromiseState::Fulfilled) { 2017 flags |= PROMISE_FLAG_FULFILLED; 2018 } 2019 promise->setFixedSlot(PromiseSlot_Flags, Int32Value(flags)); 2020 2021 // Also null out the resolve/reject functions so they can be GC'd. 2022 promise->setFixedSlot(PromiseSlot_RejectFunction, UndefinedValue()); 2023 2024 // Now that everything else is done, do the things the debugger needs. 2025 2026 // RejectPromise 2027 // Step 7. If promise.[[PromiseIsHandled]] is false, perform 2028 // HostPromiseRejectionTracker(promise, "reject"). 2029 PromiseObject::onSettled(cx, promise, unwrappedRejectionStack); 2030 2031 // FulfillPromise 2032 // Step 7. Return TriggerPromiseReactions(reactions, value). 2033 // RejectPromise 2034 // Step 8. Return TriggerPromiseReactions(reactions, reason). 2035 return TriggerPromiseReactions(cx, reactionsVal, state, valueOrReason); 2036 } 2037 2038 /** 2039 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2040 * 2041 * RejectPromise ( promise, reason ) 2042 * https://tc39.es/ecma262/#sec-rejectpromise 2043 */ 2044 [[nodiscard]] bool js::RejectPromiseInternal( 2045 JSContext* cx, JS::Handle<PromiseObject*> promise, 2046 JS::Handle<JS::Value> reason, 2047 JS::Handle<SavedFrame*> unwrappedRejectionStack /* = nullptr */) { 2048 return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected, 2049 unwrappedRejectionStack); 2050 } 2051 2052 /** 2053 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2054 * 2055 * FulfillPromise ( promise, value ) 2056 * https://tc39.es/ecma262/#sec-fulfillpromise 2057 */ 2058 [[nodiscard]] static bool FulfillMaybeWrappedPromise(JSContext* cx, 2059 HandleObject promiseObj, 2060 HandleValue value_) { 2061 Rooted<PromiseObject*> promise(cx); 2062 RootedValue value(cx, value_); 2063 2064 mozilla::Maybe<AutoRealm> ar; 2065 if (!IsProxy(promiseObj)) { 2066 promise = &promiseObj->as<PromiseObject>(); 2067 } else { 2068 JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj); 2069 if (JS_IsDeadWrapper(unwrappedPromiseObj)) { 2070 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2071 JSMSG_DEAD_OBJECT); 2072 return false; 2073 } 2074 promise = &unwrappedPromiseObj->as<PromiseObject>(); 2075 ar.emplace(cx, promise); 2076 if (!cx->compartment()->wrap(cx, &value)) { 2077 return false; 2078 } 2079 } 2080 2081 return ResolvePromise(cx, promise, value, JS::PromiseState::Fulfilled); 2082 } 2083 2084 static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp); 2085 static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp); 2086 [[nodiscard]] static PromiseObject* CreatePromiseObjectInternal( 2087 JSContext* cx, HandleObject proto = nullptr, bool protoIsWrapped = false, 2088 bool informDebugger = true); 2089 2090 enum GetCapabilitiesExecutorSlots { 2091 GetCapabilitiesExecutorSlots_Resolve, 2092 GetCapabilitiesExecutorSlots_Reject 2093 }; 2094 2095 /** 2096 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2097 * 2098 * Promise ( executor ) 2099 * https://tc39.es/ecma262/#sec-promise-executor 2100 */ 2101 [[nodiscard]] PromiseObject* js::CreatePromiseObjectWithoutResolutionFunctions( 2102 JSContext* cx) { 2103 // Steps 3-7. 2104 PromiseObject* promise = CreatePromiseObjectInternal(cx); 2105 if (!promise) { 2106 return nullptr; 2107 } 2108 2109 AddPromiseFlags(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS); 2110 2111 // Step 11. Return promise. 2112 return promise; 2113 } 2114 2115 /** 2116 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2117 * 2118 * Promise ( executor ) 2119 * https://tc39.es/ecma262/#sec-promise-executor 2120 * 2121 * As if called with GetCapabilitiesExecutor as the executor argument. 2122 */ 2123 [[nodiscard]] static PromiseObject* CreatePromiseWithDefaultResolutionFunctions( 2124 JSContext* cx, MutableHandleObject resolve, MutableHandleObject reject) { 2125 // Steps 3-7. 2126 Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx)); 2127 if (!promise) { 2128 return nullptr; 2129 } 2130 2131 // Step 8. Let resolvingFunctions be CreateResolvingFunctions(promise). 2132 if (!CreateResolvingFunctions(cx, promise, resolve, reject)) { 2133 return nullptr; 2134 } 2135 2136 promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*reject)); 2137 2138 // Step 11. Return promise. 2139 return promise; 2140 } 2141 2142 /** 2143 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2144 * 2145 * NewPromiseCapability ( C ) 2146 * https://tc39.es/ecma262/#sec-newpromisecapability 2147 */ 2148 [[nodiscard]] static bool NewPromiseCapability( 2149 JSContext* cx, HandleObject C, MutableHandle<PromiseCapability> capability, 2150 bool canOmitResolutionFunctions) { 2151 RootedValue cVal(cx, ObjectValue(*C)); 2152 2153 // Step 1. If IsConstructor(C) is false, throw a TypeError exception. 2154 // Step 2. NOTE: C is assumed to be a constructor function that supports the 2155 // parameter conventions of the Promise constructor (see 27.2.3.1). 2156 if (!IsConstructor(C)) { 2157 ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_SEARCH_STACK, cVal, 2158 nullptr); 2159 return false; 2160 } 2161 2162 // If we'd call the original Promise constructor and know that the 2163 // resolve/reject functions won't ever escape to content, we can skip 2164 // creating and calling the executor function and instead return a Promise 2165 // marked as having default resolve/reject functions. 2166 // 2167 // This can't be used in Promise.all and Promise.race because we have to 2168 // pass the reject (and resolve, in the race case) function to thenables 2169 // in the list passed to all/race, which (potentially) means exposing them 2170 // to content. 2171 // 2172 // For Promise.all and Promise.race we can only optimize away the creation 2173 // of the GetCapabilitiesExecutor function, and directly allocate the 2174 // result promise instead of invoking the Promise constructor. 2175 if (IsNativeFunction(cVal, PromiseConstructor) && 2176 cVal.toObject().nonCCWRealm() == cx->realm()) { 2177 PromiseObject* promise; 2178 if (canOmitResolutionFunctions) { 2179 promise = CreatePromiseObjectWithoutResolutionFunctions(cx); 2180 } else { 2181 promise = CreatePromiseWithDefaultResolutionFunctions( 2182 cx, capability.resolve(), capability.reject()); 2183 } 2184 if (!promise) { 2185 return false; 2186 } 2187 2188 // Step 3. Let promiseCapability be the PromiseCapability Record 2189 // { [[Promise]]: undefined, [[Resolve]]: undefined, 2190 // [[Reject]]: undefined }. 2191 capability.promise().set(promise); 2192 2193 // Step 10. Return promiseCapability. 2194 return true; 2195 } 2196 2197 // Step 4. Let executorClosure be a new Abstract Closure with parameters 2198 // (resolve, reject) that captures promiseCapability and performs the 2199 // following steps when called: 2200 Handle<PropertyName*> funName = cx->names().empty_; 2201 RootedFunction executor( 2202 cx, NewNativeFunction(cx, GetCapabilitiesExecutor, 2, funName, 2203 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 2204 if (!executor) { 2205 return false; 2206 } 2207 2208 // Step 5. Let executor be 2209 // ! CreateBuiltinFunction(executorClosure, 2, "", « »). 2210 // (omitted) 2211 2212 // Step 6. Let promise be ? Construct(C, « executor »). 2213 // Step 9. Set promiseCapability.[[Promise]] to promise. 2214 FixedConstructArgs<1> cargs(cx); 2215 cargs[0].setObject(*executor); 2216 if (!Construct(cx, cVal, cargs, cVal, capability.promise())) { 2217 return false; 2218 } 2219 2220 // Step 7. If IsCallable(promiseCapability.[[Resolve]]) is false, 2221 // throw a TypeError exception. 2222 const Value& resolveVal = 2223 executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve); 2224 if (!IsCallable(resolveVal)) { 2225 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2226 JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE); 2227 return false; 2228 } 2229 2230 // Step 8. If IsCallable(promiseCapability.[[Reject]]) is false, 2231 // throw a TypeError exception. 2232 const Value& rejectVal = 2233 executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject); 2234 if (!IsCallable(rejectVal)) { 2235 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2236 JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE); 2237 return false; 2238 } 2239 2240 // (reordered) 2241 // Step 3. Let promiseCapability be the PromiseCapability Record 2242 // { [[Promise]]: undefined, [[Resolve]]: undefined, 2243 // [[Reject]]: undefined }. 2244 capability.resolve().set(&resolveVal.toObject()); 2245 capability.reject().set(&rejectVal.toObject()); 2246 2247 // Step 10. Return promiseCapability. 2248 return true; 2249 } 2250 2251 /** 2252 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2253 * 2254 * NewPromiseCapability ( C ) 2255 * https://tc39.es/ecma262/#sec-newpromisecapability 2256 * 2257 * Steps 4.a-e. 2258 */ 2259 static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp) { 2260 CallArgs args = CallArgsFromVp(argc, vp); 2261 JSFunction* F = &args.callee().as<JSFunction>(); 2262 2263 // Step 4.a. If promiseCapability.[[Resolve]] is not undefined, 2264 // throw a TypeError exception. 2265 // Step 4.b. If promiseCapability.[[Reject]] is not undefined, 2266 // throw a TypeError exception. 2267 if (!F->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve).isUndefined() || 2268 !F->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject).isUndefined()) { 2269 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2270 JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY); 2271 return false; 2272 } 2273 2274 // Step 4.c. Set promiseCapability.[[Resolve]] to resolve. 2275 F->setExtendedSlot(GetCapabilitiesExecutorSlots_Resolve, args.get(0)); 2276 2277 // Step 4.d. Set promiseCapability.[[Reject]] to reject. 2278 F->setExtendedSlot(GetCapabilitiesExecutorSlots_Reject, args.get(1)); 2279 2280 // Step 4.e. Return undefined. 2281 args.rval().setUndefined(); 2282 return true; 2283 } 2284 2285 /** 2286 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2287 * 2288 * RejectPromise ( promise, reason ) 2289 * https://tc39.es/ecma262/#sec-rejectpromise 2290 */ 2291 [[nodiscard]] static bool RejectMaybeWrappedPromise( 2292 JSContext* cx, HandleObject promiseObj, HandleValue reason_, 2293 Handle<SavedFrame*> unwrappedRejectionStack) { 2294 Rooted<PromiseObject*> promise(cx); 2295 RootedValue reason(cx, reason_); 2296 2297 mozilla::Maybe<AutoRealm> ar; 2298 if (!IsProxy(promiseObj)) { 2299 promise = &promiseObj->as<PromiseObject>(); 2300 } else { 2301 JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj); 2302 if (JS_IsDeadWrapper(unwrappedPromiseObj)) { 2303 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2304 JSMSG_DEAD_OBJECT); 2305 return false; 2306 } 2307 promise = &unwrappedPromiseObj->as<PromiseObject>(); 2308 ar.emplace(cx, promise); 2309 2310 // The rejection reason might've been created in a compartment with higher 2311 // privileges than the Promise's. In that case, object-type rejection 2312 // values might be wrapped into a wrapper that throws whenever the 2313 // Promise's reaction handler wants to do anything useful with it. To 2314 // avoid that situation, we synthesize a generic error that doesn't 2315 // expose any privileged information but can safely be used in the 2316 // rejection handler. 2317 if (!cx->compartment()->wrap(cx, &reason)) { 2318 return false; 2319 } 2320 if (reason.isObject() && !CheckedUnwrapStatic(&reason.toObject())) { 2321 // Report the existing reason, so we don't just drop it on the 2322 // floor. 2323 JSObject* realReason = UncheckedUnwrap(&reason.toObject()); 2324 RootedValue realReasonVal(cx, ObjectValue(*realReason)); 2325 Rooted<GlobalObject*> realGlobal(cx, &realReason->nonCCWGlobal()); 2326 ReportErrorToGlobal(cx, realGlobal, realReasonVal); 2327 2328 // Async stacks are only properly adopted if there's at least one 2329 // interpreter frame active right now. If a thenable job with a 2330 // throwing `then` function got us here, that'll not be the case, 2331 // so we add one by throwing the error from self-hosted code. 2332 if (!GetInternalError(cx, JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON, 2333 &reason)) { 2334 return false; 2335 } 2336 } 2337 } 2338 2339 return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected, 2340 unwrappedRejectionStack); 2341 } 2342 2343 // Apply f to a mutable handle on each member of a collection of reactions, like 2344 // that stored in PromiseSlot_ReactionsOrResult on a pending promise. When the 2345 // reaction record is wrapped, we pass the wrapper, without dereferencing it. If 2346 // f returns false, then we stop the iteration immediately and return false. 2347 // Otherwise, we return true. 2348 // 2349 // There are several different representations for collections: 2350 // 2351 // - We represent an empty collection of reactions as an 'undefined' value. 2352 // 2353 // - We represent a collection containing a single reaction simply as the given 2354 // PromiseReactionRecord object, possibly wrapped. 2355 // 2356 // - We represent a collection of two or more reactions as a dense array of 2357 // possibly-wrapped PromiseReactionRecords. 2358 // 2359 template <typename F> 2360 static bool ForEachReaction(JSContext* cx, HandleValue reactionsVal, F f) { 2361 if (reactionsVal.isUndefined()) { 2362 return true; 2363 } 2364 2365 RootedObject reactions(cx, &reactionsVal.toObject()); 2366 RootedObject reaction(cx); 2367 2368 if (reactions->is<PromiseReactionRecord>() || IsWrapper(reactions) || 2369 JS_IsDeadWrapper(reactions)) { 2370 return f(&reactions); 2371 } 2372 2373 Handle<NativeObject*> reactionsList = reactions.as<NativeObject>(); 2374 uint32_t reactionsCount = reactionsList->getDenseInitializedLength(); 2375 MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily"); 2376 2377 for (uint32_t i = 0; i < reactionsCount; i++) { 2378 const Value& reactionVal = reactionsList->getDenseElement(i); 2379 MOZ_RELEASE_ASSERT(reactionVal.isObject()); 2380 reaction = &reactionVal.toObject(); 2381 if (!f(&reaction)) { 2382 return false; 2383 } 2384 } 2385 2386 return true; 2387 } 2388 2389 /** 2390 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2391 * 2392 * TriggerPromiseReactions ( reactions, argument ) 2393 * https://tc39.es/ecma262/#sec-triggerpromisereactions 2394 */ 2395 [[nodiscard]] static bool TriggerPromiseReactions(JSContext* cx, 2396 HandleValue reactionsVal, 2397 JS::PromiseState state, 2398 HandleValue valueOrReason) { 2399 MOZ_ASSERT(state == JS::PromiseState::Fulfilled || 2400 state == JS::PromiseState::Rejected); 2401 2402 // Step 1. For each element reaction of reactions, do 2403 // Step 2. Return undefined. 2404 return ForEachReaction(cx, reactionsVal, [&](MutableHandleObject reaction) { 2405 // Step 1.a. Let job be NewPromiseReactionJob(reaction, argument). 2406 // Step 1.b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 2407 return EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state); 2408 }); 2409 } 2410 2411 [[nodiscard]] static bool CallPromiseResolveFunction(JSContext* cx, 2412 HandleObject resolveFun, 2413 HandleValue value, 2414 HandleObject promiseObj); 2415 2416 /** 2417 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 2418 * 2419 * NewPromiseReactionJob ( reaction, argument ) 2420 * https://tc39.es/ecma262/#sec-newpromisereactionjob 2421 * 2422 * Step 1. 2423 * 2424 * Implements PromiseReactionJob optimized for the case when the reaction 2425 * handler is one of the default resolving functions as created by the 2426 * CreateResolvingFunctions abstract operation. 2427 */ 2428 [[nodiscard]] static bool DefaultResolvingPromiseReactionJob( 2429 JSContext* cx, Handle<PromiseReactionRecord*> reaction) { 2430 MOZ_ASSERT(reaction->targetState() != JS::PromiseState::Pending); 2431 2432 Rooted<PromiseObject*> promiseToResolve(cx, 2433 reaction->defaultResolvingPromise()); 2434 2435 // Testing functions allow to directly settle a promise without going 2436 // through the resolving functions. In that case the normal bookkeeping to 2437 // ensure only pending promises can be resolved doesn't apply and we need 2438 // to manually check for already settled promises. We still call 2439 // Run{Fulfill,Reject}Function for consistency with PromiseReactionJob. 2440 ResolutionMode resolutionMode = ResolveMode; 2441 RootedValue handlerResult(cx, UndefinedValue()); 2442 Rooted<SavedFrame*> unwrappedRejectionStack(cx); 2443 if (promiseToResolve->state() == JS::PromiseState::Pending) { 2444 RootedValue argument(cx, reaction->handlerArg()); 2445 2446 // Step 1.e. Else, let handlerResult be 2447 // Completion(HostCallJobCallback(handler, undefined, 2448 // « argument »)). 2449 bool ok; 2450 if (reaction->targetState() == JS::PromiseState::Fulfilled) { 2451 ok = ResolvePromiseInternal(cx, promiseToResolve, argument); 2452 } else { 2453 ok = RejectPromiseInternal(cx, promiseToResolve, argument); 2454 } 2455 2456 if (!ok) { 2457 resolutionMode = RejectMode; 2458 if (!MaybeGetAndClearExceptionAndStack(cx, &handlerResult, 2459 &unwrappedRejectionStack)) { 2460 return false; 2461 } 2462 } 2463 } 2464 2465 // Steps 1.f-i. 2466 RootedObject promiseObj(cx, reaction->promise()); 2467 RootedObject callee(cx); 2468 if (resolutionMode == ResolveMode) { 2469 callee = 2470 reaction->getFixedSlot(PromiseReactionRecord::Resolve).toObjectOrNull(); 2471 2472 return CallPromiseResolveFunction(cx, callee, handlerResult, promiseObj); 2473 } 2474 2475 callee = 2476 reaction->getFixedSlot(PromiseReactionRecord::Reject).toObjectOrNull(); 2477 2478 return CallPromiseRejectFunction(cx, callee, handlerResult, promiseObj, 2479 unwrappedRejectionStack, 2480 reaction->unhandledRejectionBehavior()); 2481 } 2482 2483 /** 2484 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2485 * 2486 * Await in async function 2487 * https://tc39.es/ecma262/#await 2488 * 2489 * Step 3. fulfilledClosure Abstract Closure. 2490 * Step 5. rejectedClosure Abstract Closure. 2491 */ 2492 [[nodiscard]] static bool AsyncFunctionPromiseReactionJob( 2493 JSContext* cx, Handle<PromiseReactionRecord*> reaction) { 2494 MOZ_ASSERT(reaction->isAsyncFunction()); 2495 2496 auto handler = static_cast<PromiseHandler>(reaction->handler().toInt32()); 2497 RootedValue argument(cx, reaction->handlerArg()); 2498 Rooted<AsyncFunctionGeneratorObject*> generator( 2499 cx, reaction->asyncFunctionGenerator()); 2500 2501 // Await's handlers don't return a value, nor throw any exceptions. 2502 // They fail only on OOM. 2503 2504 if (handler == PromiseHandler::AsyncFunctionAwaitedFulfilled) { 2505 // Step 3. fulfilledClosure Abstract Closure. 2506 return AsyncFunctionAwaitedFulfilled(cx, generator, argument); 2507 } 2508 2509 // Step 5. rejectedClosure Abstract Closure. 2510 MOZ_ASSERT(handler == PromiseHandler::AsyncFunctionAwaitedRejected); 2511 return AsyncFunctionAwaitedRejected(cx, generator, argument); 2512 } 2513 2514 /** 2515 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 2516 * 2517 * NewPromiseReactionJob ( reaction, argument ) 2518 * https://tc39.es/ecma262/#sec-newpromisereactionjob 2519 * 2520 * Step 1. 2521 * 2522 * Callback triggering the fulfill/reject reaction for a resolved Promise, 2523 * to be invoked by the embedding during its processing of the Promise job 2524 * queue. 2525 * 2526 * A PromiseReactionJob is set as the native function of an extended 2527 * JSFunction object, with all information required for the job's 2528 * execution stored in in a reaction record in its first extended slot. 2529 */ 2530 static bool PromiseReactionJob(JSContext* cx, HandleObject reactionObjIn) { 2531 RootedObject reactionObj(cx, reactionObjIn); 2532 // To ensure that the embedding ends up with the right entry global, we're 2533 // guaranteeing that the reaction job function gets created in the same 2534 // compartment as the handler function. That's not necessarily the global 2535 // that the job was triggered from, though. 2536 // We can find the triggering global via the job's reaction record. To go 2537 // back, we check if the reaction is a wrapper and if so, unwrap it and 2538 // enter its compartment. 2539 // 2540 // MG:XXX: I think that when we switch over to using the JS microtask 2541 // queue exclusively there's some cleanup around realm handling possible. 2542 mozilla::Maybe<AutoRealm> ar; 2543 if (!IsProxy(reactionObj)) { 2544 MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>()); 2545 } else { 2546 reactionObj = UncheckedUnwrap(reactionObj); 2547 if (JS_IsDeadWrapper(reactionObj)) { 2548 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2549 JSMSG_DEAD_OBJECT); 2550 return false; 2551 } 2552 MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>()); 2553 ar.emplace(cx, reactionObj); 2554 } 2555 2556 // Optimized/special cases. 2557 Handle<PromiseReactionRecord*> reaction = 2558 reactionObj.as<PromiseReactionRecord>(); 2559 if (reaction->isDefaultResolvingHandler()) { 2560 return DefaultResolvingPromiseReactionJob(cx, reaction); 2561 } 2562 if (reaction->isAsyncFunction()) { 2563 MOZ_RELEASE_ASSERT(reaction->asyncFunctionGenerator()->realm() == 2564 cx->realm()); 2565 return AsyncFunctionPromiseReactionJob(cx, reaction); 2566 } 2567 if (reaction->isAsyncGenerator()) { 2568 RootedValue argument(cx, reaction->handlerArg()); 2569 Rooted<AsyncGeneratorObject*> generator(cx, reaction->asyncGenerator()); 2570 auto handler = static_cast<PromiseHandler>(reaction->handler().toInt32()); 2571 return AsyncGeneratorPromiseReactionJob(cx, handler, generator, argument); 2572 } 2573 if (reaction->isDebuggerDummy()) { 2574 return true; 2575 } 2576 2577 // Step 1.a. Let promiseCapability be reaction.[[Capability]]. 2578 // (implicit) 2579 2580 // Step 1.c. Let handler be reaction.[[Handler]]. 2581 RootedValue handlerVal(cx, reaction->handler()); 2582 2583 RootedValue argument(cx, reaction->handlerArg()); 2584 2585 RootedValue handlerResult(cx); 2586 ResolutionMode resolutionMode = ResolveMode; 2587 2588 Rooted<SavedFrame*> unwrappedRejectionStack(cx); 2589 2590 // Step 1.d. If handler is empty, then 2591 if (handlerVal.isInt32()) { 2592 // Step 1.b. Let type be reaction.[[Type]]. 2593 // (reordered) 2594 auto handlerNum = static_cast<PromiseHandler>(handlerVal.toInt32()); 2595 2596 // Step 1.d.i. If type is Fulfill, let handlerResult be 2597 // NormalCompletion(argument). 2598 if (handlerNum == PromiseHandler::Identity) { 2599 handlerResult = argument; 2600 } else if (handlerNum == PromiseHandler::Thrower) { 2601 // Step 1.d.ii. Else, 2602 // Step 1.d.ii.1. Assert: type is Reject. 2603 // Step 1.d.ii.2. Let handlerResult be ThrowCompletion(argument). 2604 resolutionMode = RejectMode; 2605 handlerResult = argument; 2606 } 2607 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 2608 else if (handlerNum == PromiseHandler::AsyncIteratorDisposeAwaitFulfilled) { 2609 // Explicit Resource Management Proposal 2610 // 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncDispose ] ( ) 2611 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-%25asynciteratorprototype%25-%40%40asyncdispose 2612 // 2613 // Step 6.e.i. Return undefined. 2614 handlerResult = JS::UndefinedValue(); 2615 } 2616 #endif 2617 else if (handlerNum == PromiseHandler::AsyncFromSyncIteratorClose) { 2618 MOZ_ASSERT(reaction->isAsyncFromSyncIterator()); 2619 2620 // 27.1.6.4 AsyncFromSyncIteratorContinuation 2621 // 2622 // Step 13.a.i. Return ? IteratorClose(syncIteratorRecord, 2623 // ThrowCompletion(error)). 2624 // 2625 // https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation 2626 Rooted<JSObject*> iter(cx, reaction->asyncFromSyncIterator()->iterator()); 2627 MOZ_ALWAYS_TRUE(CloseIterOperation(cx, iter, CompletionKind::Throw)); 2628 2629 resolutionMode = RejectMode; 2630 handlerResult = argument; 2631 } else { 2632 // Special case for Async-from-Sync Iterator. 2633 2634 MOZ_ASSERT(handlerNum == 2635 PromiseHandler::AsyncFromSyncIteratorValueUnwrapDone || 2636 handlerNum == 2637 PromiseHandler::AsyncFromSyncIteratorValueUnwrapNotDone); 2638 2639 bool done = 2640 handlerNum == PromiseHandler::AsyncFromSyncIteratorValueUnwrapDone; 2641 2642 // 27.1.6.4 AsyncFromSyncIteratorContinuation 2643 // 2644 // Step 9.a. Return CreateIteratorResultObject(v, done). 2645 // 2646 // https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation 2647 PlainObject* resultObj = CreateIterResultObject(cx, argument, done); 2648 if (!resultObj) { 2649 return false; 2650 } 2651 2652 handlerResult = ObjectValue(*resultObj); 2653 } 2654 } else { 2655 MOZ_ASSERT(handlerVal.isObject()); 2656 MOZ_ASSERT(IsCallable(handlerVal)); 2657 2658 // Step 1.e. Else, let handlerResult be 2659 // Completion(HostCallJobCallback(handler, undefined, 2660 // « argument »)). 2661 if (!Call(cx, handlerVal, UndefinedHandleValue, argument, &handlerResult)) { 2662 resolutionMode = RejectMode; 2663 if (!MaybeGetAndClearExceptionAndStack(cx, &handlerResult, 2664 &unwrappedRejectionStack)) { 2665 return false; 2666 } 2667 } 2668 } 2669 2670 // Steps 1.f-i. 2671 RootedObject promiseObj(cx, reaction->promise()); 2672 RootedObject callee(cx); 2673 if (resolutionMode == ResolveMode) { 2674 callee = 2675 reaction->getFixedSlot(PromiseReactionRecord::Resolve).toObjectOrNull(); 2676 2677 return CallPromiseResolveFunction(cx, callee, handlerResult, promiseObj); 2678 } 2679 2680 callee = 2681 reaction->getFixedSlot(PromiseReactionRecord::Reject).toObjectOrNull(); 2682 2683 return CallPromiseRejectFunction(cx, callee, handlerResult, promiseObj, 2684 unwrappedRejectionStack, 2685 reaction->unhandledRejectionBehavior()); 2686 } 2687 2688 static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) { 2689 CallArgs args = CallArgsFromVp(argc, vp); 2690 2691 RootedFunction job(cx, &args.callee().as<JSFunction>()); 2692 2693 // Promise reactions don't return any value. 2694 args.rval().setUndefined(); 2695 2696 RootedObject reactionObj( 2697 cx, &job->getExtendedSlot(ReactionJobSlot_ReactionRecord).toObject()); 2698 return PromiseReactionJob(cx, reactionObj); 2699 } 2700 2701 /** 2702 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2703 * 2704 * NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) 2705 * https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob 2706 * 2707 * Steps 1.a-d. 2708 * 2709 * A PromiseResolveThenableJob is set as the native function of an extended 2710 * JSFunction object, with all information required for the job's 2711 * execution stored in the function's extended slots. 2712 */ 2713 static bool PromiseResolveThenableJob(JSContext* cx, HandleObject promise, 2714 HandleValue thenable, HandleObject then) { 2715 // Step 1.a. Let resolvingFunctions be 2716 // CreateResolvingFunctions(promiseToResolve). 2717 RootedObject resolveFn(cx); 2718 RootedObject rejectFn(cx); 2719 if (!CreateResolvingFunctions(cx, promise, &resolveFn, &rejectFn)) { 2720 return false; 2721 } 2722 2723 // Step 1.b. Let thenCallResult be 2724 // HostCallJobCallback(then, thenable, 2725 // « resolvingFunctions.[[Resolve]], 2726 // resolvingFunctions.[[Reject]] »). 2727 FixedInvokeArgs<2> args2(cx); 2728 args2[0].setObject(*resolveFn); 2729 args2[1].setObject(*rejectFn); 2730 2731 // In difference to the usual pattern, we return immediately on success. 2732 RootedValue rval(cx); 2733 if (Call(cx, thenable, then, args2, &rval)) { 2734 // Step 1.d. Return Completion(thenCallResult). 2735 return true; 2736 } 2737 2738 // Step 1.c. If thenCallResult is an abrupt completion, then 2739 2740 Rooted<SavedFrame*> stack(cx); 2741 if (!MaybeGetAndClearExceptionAndStack(cx, &rval, &stack)) { 2742 return false; 2743 } 2744 2745 // Step 1.c.i. Let status be 2746 // Call(resolvingFunctions.[[Reject]], undefined, 2747 // « thenCallResult.[[Value]] »). 2748 // Step 1.c.ii. Return Completion(status). 2749 RootedValue rejectVal(cx, ObjectValue(*rejectFn)); 2750 return Call(cx, rejectVal, UndefinedHandleValue, rval, &rval); 2751 } 2752 2753 /* 2754 * Usage of the function's extended slots is described in the ThenableJobSlots 2755 * enum. 2756 */ 2757 static bool PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp) { 2758 CallArgs args = CallArgsFromVp(argc, vp); 2759 2760 RootedFunction job(cx, &args.callee().as<JSFunction>()); 2761 RootedObject promise( 2762 cx, &job->getExtendedSlot(ThenableJobSlot_Promise).toObject()); 2763 RootedValue thenable(cx, job->getExtendedSlot(ThenableJobSlot_Thenable)); 2764 RootedObject then(cx, 2765 &job->getExtendedSlot(ThenableJobSlot_Handler).toObject()); 2766 2767 return PromiseResolveThenableJob(cx, promise, thenable, then); 2768 } 2769 2770 [[nodiscard]] static bool OriginalPromiseThenWithoutSettleHandlers( 2771 JSContext* cx, Handle<PromiseObject*> promise, 2772 Handle<PromiseObject*> promiseToResolve); 2773 2774 /** 2775 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2776 * 2777 * NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) 2778 * https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob 2779 * 2780 * Step 1.a-d. 2781 * 2782 * Specialization of PromiseResolveThenableJob when the `thenable` is a 2783 * built-in Promise object and the `then` property is the built-in 2784 * `Promise.prototype.then` function. 2785 * 2786 * A PromiseResolveBuiltinThenableJob is set as the native function of an 2787 * extended JSFunction object, with all information required for the job's 2788 * execution stored in the function's extended slots. 2789 * 2790 * Usage of the function's extended slots is described in the ThenableJobSlots 2791 * enum. 2792 */ 2793 static bool PromiseResolveBuiltinThenableJob(JSContext* cx, 2794 HandleObject promise, 2795 HandleObject thenable) { 2796 cx->check(promise, thenable); 2797 MOZ_ASSERT(promise->is<PromiseObject>()); 2798 MOZ_ASSERT(thenable->is<PromiseObject>()); 2799 2800 // Step 1.a. Let resolvingFunctions be 2801 // CreateResolvingFunctions(promiseToResolve). 2802 // (skipped) 2803 2804 // Step 1.b. Let thenCallResult be HostCallJobCallback( 2805 // then, thenable, 2806 // « resolvingFunctions.[[Resolve]], 2807 // resolvingFunctions.[[Reject]] »). 2808 // 2809 // NOTE: In difference to the usual pattern, we return immediately on success. 2810 if (OriginalPromiseThenWithoutSettleHandlers(cx, thenable.as<PromiseObject>(), 2811 promise.as<PromiseObject>())) { 2812 // Step 1.d. Return Completion(thenCallResult). 2813 return true; 2814 } 2815 2816 // Step 1.c. If thenCallResult is an abrupt completion, then 2817 RootedValue exception(cx); 2818 Rooted<SavedFrame*> stack(cx); 2819 if (!MaybeGetAndClearExceptionAndStack(cx, &exception, &stack)) { 2820 return false; 2821 } 2822 2823 // Testing functions allow to directly settle a promise without going 2824 // through the resolving functions. In that case the normal bookkeeping to 2825 // ensure only pending promises can be resolved doesn't apply and we need 2826 // to manually check for already settled promises. The exception is simply 2827 // dropped when this case happens. 2828 if (promise->as<PromiseObject>().state() != JS::PromiseState::Pending) { 2829 return true; 2830 } 2831 2832 // Step 1.c.i. Let status be 2833 // Call(resolvingFunctions.[[Reject]], undefined, 2834 // « thenCallResult.[[Value]] »). 2835 // Step 1.c.ii. Return Completion(status). 2836 return RejectPromiseInternal(cx, promise.as<PromiseObject>(), exception, 2837 stack); 2838 } 2839 2840 static bool PromiseResolveBuiltinThenableJob(JSContext* cx, unsigned argc, 2841 Value* vp) { 2842 CallArgs args = CallArgsFromVp(argc, vp); 2843 2844 RootedFunction job(cx, &args.callee().as<JSFunction>()); 2845 RootedObject promise( 2846 cx, &job->getExtendedSlot(ThenableJobSlot_Promise).toObject()); 2847 RootedObject thenable( 2848 cx, &job->getExtendedSlot(ThenableJobSlot_Thenable).toObject()); 2849 // The handler slot is not used for builtin `then`. 2850 MOZ_ASSERT(job->getExtendedSlot(ThenableJobSlot_Handler).isUndefined()); 2851 2852 return PromiseResolveBuiltinThenableJob(cx, promise, thenable); 2853 } 2854 2855 /** 2856 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2857 * 2858 * NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) 2859 * https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob 2860 * HostEnqueuePromiseJob ( job, realm ) 2861 * https://tc39.es/ecma262/#sec-hostenqueuepromisejob 2862 * 2863 * Tells the embedding to enqueue a Promise resolve thenable job, based on 2864 * three parameters: 2865 * promiseToResolve_ - The promise to resolve, obviously. 2866 * thenable_ - The thenable to resolve the Promise with. 2867 * thenVal - The `then` function to invoke with the `thenable` as the receiver. 2868 */ 2869 [[nodiscard]] static bool EnqueuePromiseResolveThenableJob( 2870 JSContext* cx, HandleValue promiseToResolve_, HandleValue thenable_, 2871 HandleValue thenVal) { 2872 // Need to re-root these to enable wrapping them below. 2873 RootedValue promiseToResolve(cx, promiseToResolve_); 2874 RootedValue thenable(cx, thenable_); 2875 2876 // Step 2. Let getThenRealmResult be GetFunctionRealm(then.[[Callback]]). 2877 // Step 3. If getThenRealmResult is a normal completion, let thenRealm be 2878 // getThenRealmResult.[[Value]]. 2879 // Step 4. Else, let thenRealm be the current Realm Record. 2880 // Step 5. NOTE: thenRealm is never null. When then.[[Callback]] is a revoked 2881 // Proxy and no code runs, thenRealm is used to create error objects. 2882 // 2883 // NOTE: Instead of passing job and realm separately, we use the job's 2884 // JSFunction object's realm as the job's realm. 2885 // So we should enter the thenRealm before creating the job function. 2886 // 2887 // GetFunctionRealm performed inside AutoFunctionOrCurrentRealm uses checked 2888 // unwrap and this is fine given the behavior difference (see the comment 2889 // around AutoFunctionOrCurrentRealm usage in EnqueuePromiseReactionJob for 2890 // more details) is observable only when the `thenable` is from content realm 2891 // and `then` is from chrome realm, that shouldn't happen in practice. 2892 // 2893 // NOTE: If `thenable` is also from chrome realm, accessing `then` silently 2894 // fails and it returns `undefined`, and that case doesn't reach here. 2895 RootedObject then(cx, &thenVal.toObject()); 2896 AutoFunctionOrCurrentRealm ar(cx, then); 2897 if (then->maybeCCWRealm() != cx->realm()) { 2898 if (!cx->compartment()->wrap(cx, &then)) { 2899 return false; 2900 } 2901 } 2902 2903 // Wrap the `promiseToResolve` and `thenable` arguments. 2904 if (!cx->compartment()->wrap(cx, &promiseToResolve)) { 2905 return false; 2906 } 2907 2908 MOZ_ASSERT(thenable.isObject()); 2909 if (!cx->compartment()->wrap(cx, &thenable)) { 2910 return false; 2911 } 2912 2913 // At this point the promise is guaranteed to be wrapped into the job's 2914 // compartment. 2915 RootedObject promise(cx, &promiseToResolve.toObject()); 2916 2917 if (JS::Prefs::use_js_microtask_queue()) { 2918 RootedObject hostDefinedGlobalRepresentative(cx); 2919 { 2920 RootedObject hostDefinedGlobal(cx); 2921 if (!cx->jobQueue->getHostDefinedGlobal(cx, &hostDefinedGlobal)) { 2922 return false; 2923 } 2924 2925 MOZ_ASSERT_IF(hostDefinedGlobal, hostDefinedGlobal->is<GlobalObject>()); 2926 if (hostDefinedGlobal) { 2927 hostDefinedGlobalRepresentative = 2928 &hostDefinedGlobal->as<GlobalObject>().getObjectPrototype(); 2929 } 2930 } 2931 2932 // Wrap the representative. 2933 if (!cx->compartment()->wrap(cx, &hostDefinedGlobalRepresentative)) { 2934 return false; 2935 } 2936 2937 ThenableJob* thenableJob = 2938 NewThenableJob(cx, ThenableJob::PromiseResolveThenableJob, promise, 2939 thenable, then, HostDefinedDataIsOptimizedOut); 2940 if (!thenableJob) { 2941 return false; 2942 } 2943 2944 thenableJob->setHostDefinedGlobalRepresentative( 2945 hostDefinedGlobalRepresentative); 2946 return EnqueueJob(cx, thenableJob); 2947 } 2948 2949 // Step 1. Let job be a new Job Abstract Closure with no parameters that 2950 // captures promiseToResolve, thenable, and then and performs the 2951 // following steps when called: 2952 Handle<PropertyName*> funName = cx->names().empty_; 2953 RootedFunction job( 2954 cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName, 2955 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 2956 if (!job) { 2957 return false; 2958 } 2959 2960 // Set the `promiseToResolve`, `thenable` and `then` arguments on the 2961 // callback. 2962 job->setExtendedSlot(ThenableJobSlot_Promise, promiseToResolve); 2963 job->setExtendedSlot(ThenableJobSlot_Thenable, thenable); 2964 job->setExtendedSlot(ThenableJobSlot_Handler, ObjectValue(*then)); 2965 2966 // Step X. HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 2967 return cx->runtime()->enqueuePromiseJob(cx, job, promise, 2968 HostDefinedDataIsOptimizedOut); 2969 } 2970 2971 /** 2972 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 2973 * 2974 * NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) 2975 * https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob 2976 * HostEnqueuePromiseJob ( job, realm ) 2977 * https://tc39.es/ecma262/#sec-hostenqueuepromisejob 2978 * 2979 * Tells the embedding to enqueue a Promise resolve thenable built-in job, 2980 * based on two parameters: 2981 * promiseToResolve - The promise to resolve, obviously. 2982 * thenable - The thenable to resolve the Promise with. 2983 */ 2984 [[nodiscard]] static bool EnqueuePromiseResolveThenableBuiltinJob( 2985 JSContext* cx, HandleObject promiseToResolve, HandleObject thenable) { 2986 cx->check(promiseToResolve, thenable); 2987 MOZ_ASSERT(promiseToResolve->is<PromiseObject>()); 2988 MOZ_ASSERT(thenable->is<PromiseObject>()); 2989 2990 if (JS::Prefs::use_js_microtask_queue()) { 2991 // Step 1. Let job be a new Job Abstract Closure with no parameters that 2992 2993 Rooted<JSObject*> hostDefinedData(cx); 2994 if (!cx->runtime()->getHostDefinedData(cx, &hostDefinedData)) { 2995 return false; 2996 } 2997 2998 RootedValue thenableValue(cx, ObjectValue(*thenable)); 2999 ThenableJob* thenableJob = NewThenableJob( 3000 cx, ThenableJob::PromiseResolveBuiltinThenableJob, promiseToResolve, 3001 thenableValue, nullptr, hostDefinedData); 3002 if (!thenableJob) { 3003 return false; 3004 } 3005 3006 return EnqueueJob(cx, thenableJob); 3007 } 3008 3009 // Step 1. Let job be a new Job Abstract Closure with no parameters that 3010 // captures promiseToResolve, thenable, and then and performs the 3011 // following steps when called: 3012 Handle<PropertyName*> funName = cx->names().empty_; 3013 RootedFunction job( 3014 cx, NewNativeFunction(cx, PromiseResolveBuiltinThenableJob, 0, funName, 3015 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 3016 if (!job) { 3017 return false; 3018 } 3019 3020 // Steps 2-5. 3021 // (implicit) 3022 // `then` is built-in Promise.prototype.then in the current realm., 3023 // thus `thenRealm` is also current realm, and we have nothing to do here. 3024 3025 // Store the promise and the thenable on the reaction job. 3026 job->setExtendedSlot(ThenableJobSlot_Promise, ObjectValue(*promiseToResolve)); 3027 job->setExtendedSlot(ThenableJobSlot_Thenable, ObjectValue(*thenable)); 3028 3029 Rooted<JSObject*> hostDefinedData(cx); 3030 if (!cx->runtime()->getHostDefinedData(cx, &hostDefinedData)) { 3031 return false; 3032 } 3033 3034 // HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). 3035 return cx->runtime()->enqueuePromiseJob(cx, job, promiseToResolve, 3036 hostDefinedData); 3037 } 3038 3039 [[nodiscard]] static bool AddDummyPromiseReactionForDebugger( 3040 JSContext* cx, Handle<PromiseObject*> promise, 3041 HandleObject dependentPromise); 3042 3043 [[nodiscard]] static bool AddPromiseReaction( 3044 JSContext* cx, Handle<PromiseObject*> promise, 3045 Handle<PromiseReactionRecord*> reaction); 3046 3047 static JSFunction* GetResolveFunctionFromReject(JSFunction* reject) { 3048 MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction); 3049 Value resolveFunVal = 3050 reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction); 3051 MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction)); 3052 return &resolveFunVal.toObject().as<JSFunction>(); 3053 } 3054 3055 static JSFunction* GetRejectFunctionFromResolve(JSFunction* resolve) { 3056 MOZ_ASSERT(resolve->maybeNative() == ResolvePromiseFunction); 3057 Value rejectFunVal = 3058 resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction); 3059 MOZ_ASSERT(IsNativeFunction(rejectFunVal, RejectPromiseFunction)); 3060 return &rejectFunVal.toObject().as<JSFunction>(); 3061 } 3062 3063 static JSFunction* GetResolveFunctionFromPromise(PromiseObject* promise) { 3064 Value rejectFunVal = promise->getFixedSlot(PromiseSlot_RejectFunction); 3065 if (rejectFunVal.isUndefined()) { 3066 return nullptr; 3067 } 3068 JSObject* rejectFunObj = &rejectFunVal.toObject(); 3069 3070 // We can safely unwrap it because all we want is to get the resolve 3071 // function. 3072 if (IsWrapper(rejectFunObj)) { 3073 rejectFunObj = UncheckedUnwrap(rejectFunObj); 3074 } 3075 3076 if (!rejectFunObj->is<JSFunction>()) { 3077 return nullptr; 3078 } 3079 3080 JSFunction* rejectFun = &rejectFunObj->as<JSFunction>(); 3081 3082 // Only the original RejectPromiseFunction has a reference to the resolve 3083 // function. 3084 if (rejectFun->maybeNative() != &RejectPromiseFunction) { 3085 return nullptr; 3086 } 3087 3088 // The reject function was already called and cleared its resolve-function 3089 // extended slot. 3090 if (rejectFun->getExtendedSlot(RejectFunctionSlot_ResolveFunction) 3091 .isUndefined()) { 3092 return nullptr; 3093 } 3094 3095 return GetResolveFunctionFromReject(rejectFun); 3096 } 3097 3098 /** 3099 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3100 * 3101 * Promise ( executor ) 3102 * https://tc39.es/ecma262/#sec-promise-executor 3103 * 3104 * Steps 3-7. 3105 */ 3106 [[nodiscard]] static MOZ_ALWAYS_INLINE PromiseObject* 3107 CreatePromiseObjectInternal(JSContext* cx, HandleObject proto /* = nullptr */, 3108 bool protoIsWrapped /* = false */, 3109 bool informDebugger /* = true */) { 3110 // Enter the unwrapped proto's compartment, if that's different from 3111 // the current one. 3112 // All state stored in a Promise's fixed slots must be created in the 3113 // same compartment, so we get all of that out of the way here. 3114 // (Except for the resolution functions, which are created below.) 3115 mozilla::Maybe<AutoRealm> ar; 3116 if (protoIsWrapped) { 3117 ar.emplace(cx, proto); 3118 } 3119 3120 // Step 3. Let promise be 3121 // ? OrdinaryCreateFromConstructor( 3122 // NewTarget, "%Promise.prototype%", 3123 // « [[PromiseState]], [[PromiseResult]], 3124 // [[PromiseFulfillReactions]], [[PromiseRejectReactions]], 3125 // [[PromiseIsHandled]] »). 3126 PromiseObject* promise = NewObjectWithClassProto<PromiseObject>(cx, proto); 3127 if (!promise) { 3128 return nullptr; 3129 } 3130 3131 // Step 4. Set promise.[[PromiseState]] to pending. 3132 promise->initFixedSlot(PromiseSlot_Flags, Int32Value(0)); 3133 3134 // Step 5. Set promise.[[PromiseFulfillReactions]] to a new empty List. 3135 // Step 6. Set promise.[[PromiseRejectReactions]] to a new empty List. 3136 // (omitted) 3137 // We allocate our single list of reaction records lazily. 3138 3139 // Step 7. Set promise.[[PromiseIsHandled]] to false. 3140 // (implicit) 3141 // The handled flag is unset by default. 3142 3143 if (MOZ_LIKELY(!JS::IsAsyncStackCaptureEnabledForRealm(cx))) { 3144 return promise; 3145 } 3146 3147 // Store an allocation stack so we can later figure out what the 3148 // control flow was for some unexpected results. Frightfully expensive, 3149 // but oh well. 3150 3151 Rooted<PromiseObject*> promiseRoot(cx, promise); 3152 3153 PromiseDebugInfo* debugInfo = PromiseDebugInfo::create(cx, promiseRoot); 3154 if (!debugInfo) { 3155 return nullptr; 3156 } 3157 3158 // Let the Debugger know about this Promise. 3159 if (informDebugger) { 3160 DebugAPI::onNewPromise(cx, promiseRoot); 3161 } 3162 3163 return promiseRoot; 3164 } 3165 3166 /** 3167 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3168 * 3169 * Promise ( executor ) 3170 * https://tc39.es/ecma262/#sec-promise-executor 3171 */ 3172 static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp) { 3173 CallArgs args = CallArgsFromVp(argc, vp); 3174 3175 // Step 1. If NewTarget is undefined, throw a TypeError exception. 3176 if (!ThrowIfNotConstructing(cx, args, "Promise")) { 3177 return false; 3178 } 3179 3180 // Step 2. If IsCallable(executor) is false, throw a TypeError exception. 3181 HandleValue executorVal = args.get(0); 3182 if (!IsCallable(executorVal)) { 3183 return ReportIsNotFunction(cx, executorVal); 3184 } 3185 RootedObject executor(cx, &executorVal.toObject()); 3186 3187 RootedObject newTarget(cx, &args.newTarget().toObject()); 3188 3189 // If the constructor is called via an Xray wrapper, then the newTarget 3190 // hasn't been unwrapped. We want that because, while the actual instance 3191 // should be created in the target compartment, the constructor's code 3192 // should run in the wrapper's compartment. 3193 // 3194 // This is so that the resolve and reject callbacks get created in the 3195 // wrapper's compartment, which is required for code in that compartment 3196 // to freely interact with it, and, e.g., pass objects as arguments, which 3197 // it wouldn't be able to if the callbacks were themselves wrapped in Xray 3198 // wrappers. 3199 // 3200 // At the same time, just creating the Promise itself in the wrapper's 3201 // compartment wouldn't be helpful: if the wrapper forbids interactions 3202 // with objects except for specific actions, such as calling them, then 3203 // the code we want to expose it to can't actually treat it as a Promise: 3204 // calling .then on it would throw, for example. 3205 // 3206 // Another scenario where it's important to create the Promise in a 3207 // different compartment from the resolution functions is when we want to 3208 // give non-privileged code a Promise resolved with the result of a 3209 // Promise from privileged code; as a return value of a JS-implemented 3210 // API, say. If the resolution functions were unprivileged, then resolving 3211 // with a privileged Promise would cause `resolve` to attempt accessing 3212 // .then on the passed Promise, which would throw an exception, so we'd 3213 // just end up with a rejected Promise. Really, we want to chain the two 3214 // Promises, with the unprivileged one resolved with the resolution of the 3215 // privileged one. 3216 3217 bool needsWrapping = false; 3218 RootedObject proto(cx); 3219 if (IsWrapper(newTarget)) { 3220 JSObject* unwrappedNewTarget = CheckedUnwrapStatic(newTarget); 3221 MOZ_ASSERT(unwrappedNewTarget); 3222 MOZ_ASSERT(unwrappedNewTarget != newTarget); 3223 3224 newTarget = unwrappedNewTarget; 3225 { 3226 AutoRealm ar(cx, newTarget); 3227 Handle<GlobalObject*> global = cx->global(); 3228 JSObject* promiseCtor = 3229 GlobalObject::getOrCreatePromiseConstructor(cx, global); 3230 if (!promiseCtor) { 3231 return false; 3232 } 3233 3234 // Promise subclasses don't get the special Xray treatment, so 3235 // we only need to do the complex wrapping and unwrapping scheme 3236 // described above for instances of Promise itself. 3237 if (newTarget == promiseCtor) { 3238 needsWrapping = true; 3239 proto = GlobalObject::getOrCreatePromisePrototype(cx, cx->global()); 3240 if (!proto) { 3241 return false; 3242 } 3243 } 3244 } 3245 } 3246 3247 if (needsWrapping) { 3248 if (!cx->compartment()->wrap(cx, &proto)) { 3249 return false; 3250 } 3251 } else { 3252 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_Promise, 3253 &proto)) { 3254 return false; 3255 } 3256 } 3257 PromiseObject* promise = 3258 PromiseObject::create(cx, executor, proto, needsWrapping); 3259 if (!promise) { 3260 return false; 3261 } 3262 3263 // Step 11. 3264 args.rval().setObject(*promise); 3265 if (needsWrapping) { 3266 return cx->compartment()->wrap(cx, args.rval()); 3267 } 3268 return true; 3269 } 3270 3271 bool js::IsPromiseConstructor(const JSObject* obj) { 3272 // Note: this also returns true for cross-realm Promise constructors in the 3273 // same compartment. 3274 return IsNativeFunction(obj, PromiseConstructor); 3275 } 3276 3277 /** 3278 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3279 * 3280 * Promise ( executor ) 3281 * https://tc39.es/ecma262/#sec-promise-executor 3282 * 3283 * Steps 3-11. 3284 */ 3285 /* static */ 3286 PromiseObject* PromiseObject::create(JSContext* cx, HandleObject executor, 3287 HandleObject proto /* = nullptr */, 3288 bool needsWrapping /* = false */) { 3289 MOZ_ASSERT(executor->isCallable()); 3290 3291 RootedTuple<JSObject*, PromiseObject*, JSObject*, JSObject*, JSObject*, 3292 JSObject*, Value, Value, SavedFrame*, Value> 3293 roots(cx); 3294 RootedField<JSObject*, 0> usedProto(roots, proto); 3295 // If the proto is wrapped, that means the current function is running 3296 // with a different compartment active from the one the Promise instance 3297 // is to be created in. 3298 // See the comment in PromiseConstructor for details. 3299 if (needsWrapping) { 3300 MOZ_ASSERT(proto); 3301 usedProto = CheckedUnwrapStatic(proto); 3302 if (!usedProto) { 3303 ReportAccessDenied(cx); 3304 return nullptr; 3305 } 3306 } 3307 3308 // Steps 3-7. 3309 RootedField<PromiseObject*, 1> promise( 3310 roots, CreatePromiseObjectInternal(cx, usedProto, needsWrapping, false)); 3311 if (!promise) { 3312 return nullptr; 3313 } 3314 3315 RootedField<JSObject*, 2> promiseObj(roots, promise); 3316 if (needsWrapping && !cx->compartment()->wrap(cx, &promiseObj)) { 3317 return nullptr; 3318 } 3319 3320 // Step 8. Let resolvingFunctions be CreateResolvingFunctions(promise). 3321 // 3322 // The resolving functions are created in the compartment active when the 3323 // (maybe wrapped) Promise constructor was called. They contain checks and 3324 // can unwrap the Promise if required. 3325 RootedField<JSObject*, 3> resolveFn(roots); 3326 RootedField<JSObject*, 4> rejectFn(roots); 3327 if (!CreateResolvingFunctions(cx, promiseObj, &resolveFn, &rejectFn)) { 3328 return nullptr; 3329 } 3330 3331 // Need to wrap the resolution functions before storing them on the Promise. 3332 MOZ_ASSERT(promise->getFixedSlot(PromiseSlot_RejectFunction).isUndefined(), 3333 "Slot must be undefined so initFixedSlot can be used"); 3334 if (needsWrapping) { 3335 AutoRealm ar(cx, promise); 3336 RootedField<JSObject*, 5> wrappedRejectFn(roots, rejectFn); 3337 if (!cx->compartment()->wrap(cx, &wrappedRejectFn)) { 3338 return nullptr; 3339 } 3340 promise->initFixedSlot(PromiseSlot_RejectFunction, 3341 ObjectValue(*wrappedRejectFn)); 3342 } else { 3343 promise->initFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*rejectFn)); 3344 } 3345 3346 // Step 9. Let completion be 3347 // Call(executor, undefined, « resolvingFunctions.[[Resolve]], 3348 // resolvingFunctions.[[Reject]] »). 3349 bool success; 3350 { 3351 FixedInvokeArgs<2> args(cx); 3352 args[0].setObject(*resolveFn); 3353 args[1].setObject(*rejectFn); 3354 3355 RootedField<Value, 6> calleeOrRval(roots, ObjectValue(*executor)); 3356 success = Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval); 3357 } 3358 3359 // Step 10. If completion is an abrupt completion, then 3360 if (!success) { 3361 RootedField<Value, 7> exceptionVal(roots); 3362 RootedField<SavedFrame*, 8> stack(roots); 3363 if (!MaybeGetAndClearExceptionAndStack(cx, &exceptionVal, &stack)) { 3364 return nullptr; 3365 } 3366 3367 // Step 10.a. Perform 3368 // ? Call(resolvingFunctions.[[Reject]], undefined, 3369 // « completion.[[Value]] »). 3370 RootedField<Value, 9> calleeOrRval(roots, ObjectValue(*rejectFn)); 3371 if (!Call(cx, calleeOrRval, UndefinedHandleValue, exceptionVal, 3372 &calleeOrRval)) { 3373 return nullptr; 3374 } 3375 } 3376 3377 // Let the Debugger know about this Promise. 3378 DebugAPI::onNewPromise(cx, promise); 3379 3380 // Step 11. Return promise. 3381 return promise; 3382 } 3383 3384 /** 3385 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3386 * 3387 * Promise ( executor ) 3388 * https://tc39.es/ecma262/#sec-promise-executor 3389 * 3390 * skipping creation of resolution functions and executor function invocation. 3391 */ 3392 /* static */ 3393 PromiseObject* PromiseObject::createSkippingExecutor(JSContext* cx) { 3394 return CreatePromiseObjectWithoutResolutionFunctions(cx); 3395 } 3396 3397 class MOZ_STACK_CLASS PromiseForOfIterator : public JS::ForOfIterator { 3398 public: 3399 using JS::ForOfIterator::ForOfIterator; 3400 3401 bool isOptimizedDenseArrayIteration() { 3402 MOZ_ASSERT(valueIsIterable()); 3403 return index != NOT_ARRAY && IsPackedArray(iterator); 3404 } 3405 }; 3406 3407 [[nodiscard]] static bool PerformPromiseAll( 3408 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 3409 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 3410 bool* done); 3411 3412 [[nodiscard]] static bool PerformPromiseAllSettled( 3413 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 3414 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 3415 bool* done); 3416 3417 [[nodiscard]] static bool PerformPromiseAny( 3418 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 3419 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 3420 bool* done); 3421 3422 [[nodiscard]] static bool PerformPromiseRace( 3423 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 3424 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 3425 bool* done); 3426 3427 enum class CombinatorKind { All, AllSettled, Any, Race }; 3428 3429 /** 3430 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3431 * 3432 * Unified implementation of 3433 * 3434 * Promise.all ( iterable ) 3435 * https://tc39.es/ecma262/#sec-promise.all 3436 * Promise.allSettled ( iterable ) 3437 * https://tc39.es/ecma262/#sec-promise.allsettled 3438 * Promise.race ( iterable ) 3439 * https://tc39.es/ecma262/#sec-promise.race 3440 * Promise.any ( iterable ) 3441 * https://tc39.es/ecma262/#sec-promise.any 3442 * GetPromiseResolve ( promiseConstructor ) 3443 * https://tc39.es/ecma262/#sec-getpromiseresolve 3444 */ 3445 [[nodiscard]] static bool CommonPromiseCombinator(JSContext* cx, CallArgs& args, 3446 CombinatorKind kind) { 3447 HandleValue iterable = args.get(0); 3448 3449 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 3450 // (moved from NewPromiseCapability, step 1). 3451 HandleValue CVal = args.thisv(); 3452 if (!CVal.isObject()) { 3453 const char* message; 3454 switch (kind) { 3455 case CombinatorKind::All: 3456 message = "Receiver of Promise.all call"; 3457 break; 3458 case CombinatorKind::AllSettled: 3459 message = "Receiver of Promise.allSettled call"; 3460 break; 3461 case CombinatorKind::Any: 3462 message = "Receiver of Promise.any call"; 3463 break; 3464 case CombinatorKind::Race: 3465 message = "Receiver of Promise.race call"; 3466 break; 3467 } 3468 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3469 JSMSG_OBJECT_REQUIRED, message); 3470 return false; 3471 } 3472 3473 // Step 1. Let C be the this value. 3474 RootedObject C(cx, &CVal.toObject()); 3475 3476 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 3477 Rooted<PromiseCapability> promiseCapability(cx); 3478 if (!NewPromiseCapability(cx, C, &promiseCapability, false)) { 3479 return false; 3480 } 3481 3482 RootedValue promiseResolve(cx, UndefinedValue()); 3483 { 3484 JSObject* promiseCtor = 3485 GlobalObject::getOrCreatePromiseConstructor(cx, cx->global()); 3486 if (!promiseCtor) { 3487 return false; 3488 } 3489 3490 if (C != promiseCtor || !HasDefaultPromiseProperties(cx)) { 3491 // Step 3. Let promiseResolve be GetPromiseResolve(C). 3492 3493 // GetPromiseResolve 3494 // Step 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). 3495 if (!GetProperty(cx, C, C, cx->names().resolve, &promiseResolve)) { 3496 // Step 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). 3497 return AbruptRejectPromise(cx, args, promiseCapability); 3498 } 3499 3500 // GetPromiseResolve 3501 // Step 2. If IsCallable(promiseResolve) is false, 3502 // throw a TypeError exception. 3503 if (!IsCallable(promiseResolve)) { 3504 ReportIsNotFunction(cx, promiseResolve); 3505 3506 // Step 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). 3507 return AbruptRejectPromise(cx, args, promiseCapability); 3508 } 3509 } 3510 } 3511 3512 // Step 5. Let iteratorRecord be GetIterator(iterable). 3513 PromiseForOfIterator iter(cx); 3514 if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable)) { 3515 // Step 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). 3516 return AbruptRejectPromise(cx, args, promiseCapability); 3517 } 3518 3519 if (!iter.valueIsIterable()) { 3520 // Step 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). 3521 const char* message; 3522 switch (kind) { 3523 case CombinatorKind::All: 3524 message = "Argument of Promise.all"; 3525 break; 3526 case CombinatorKind::AllSettled: 3527 message = "Argument of Promise.allSettled"; 3528 break; 3529 case CombinatorKind::Any: 3530 message = "Argument of Promise.any"; 3531 break; 3532 case CombinatorKind::Race: 3533 message = "Argument of Promise.race"; 3534 break; 3535 } 3536 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, 3537 message); 3538 return AbruptRejectPromise(cx, args, promiseCapability); 3539 } 3540 3541 bool done, result; 3542 switch (kind) { 3543 case CombinatorKind::All: 3544 // Promise.all 3545 // Step 7. Let result be 3546 // PerformPromiseAll(iteratorRecord, C, promiseCapability, 3547 // promiseResolve). 3548 result = PerformPromiseAll(cx, iter, C, promiseCapability, promiseResolve, 3549 &done); 3550 break; 3551 case CombinatorKind::AllSettled: 3552 // Promise.allSettled 3553 // Step 7. Let result be 3554 // PerformPromiseAllSettled(iteratorRecord, C, promiseCapability, 3555 // promiseResolve). 3556 result = PerformPromiseAllSettled(cx, iter, C, promiseCapability, 3557 promiseResolve, &done); 3558 break; 3559 case CombinatorKind::Any: 3560 // Promise.any 3561 // Step 7. Let result be 3562 // PerformPromiseAny(iteratorRecord, C, promiseCapability, 3563 // promiseResolve). 3564 result = PerformPromiseAny(cx, iter, C, promiseCapability, promiseResolve, 3565 &done); 3566 break; 3567 case CombinatorKind::Race: 3568 // Promise.race 3569 // Step 7. Let result be 3570 // PerformPromiseRace(iteratorRecord, C, promiseCapability, 3571 // promiseResolve). 3572 result = PerformPromiseRace(cx, iter, C, promiseCapability, 3573 promiseResolve, &done); 3574 break; 3575 } 3576 3577 // Step 8. If result is an abrupt completion, then 3578 if (!result) { 3579 // Step 8.a. If iteratorRecord.[[Done]] is false, 3580 // set result to IteratorClose(iteratorRecord, result). 3581 if (!done) { 3582 iter.closeThrow(); 3583 } 3584 3585 // Step 8.b. IfAbruptRejectPromise(result, promiseCapability). 3586 return AbruptRejectPromise(cx, args, promiseCapability); 3587 } 3588 3589 // Step 9. Return Completion(result). 3590 args.rval().setObject(*promiseCapability.promise()); 3591 return true; 3592 } 3593 3594 /** 3595 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3596 * 3597 * Promise.all ( iterable ) 3598 * https://tc39.es/ecma262/#sec-promise.all 3599 */ 3600 static bool Promise_static_all(JSContext* cx, unsigned argc, Value* vp) { 3601 CallArgs args = CallArgsFromVp(argc, vp); 3602 return CommonPromiseCombinator(cx, args, CombinatorKind::All); 3603 } 3604 3605 #ifdef NIGHTLY_BUILD 3606 /** 3607 * Await Dictionary Proposal 3608 * 3609 * Promise.allKeyed 3610 * https://tc39.es/proposal-await-dictionary/#sec-promise.allkeyed 3611 */ 3612 static bool Promise_static_allKeyed(JSContext* cx, unsigned argc, Value* vp) { 3613 JS_ReportErrorASCII(cx, "Promise.allKeyed is not yet implemented"); 3614 return false; 3615 } 3616 3617 /** 3618 * Await Dictionary Proposal 3619 * 3620 * Promise.allSettledKeyed 3621 * https://tc39.es/proposal-await-dictionary/#sec-promise.allsettledkeyed 3622 */ 3623 static bool Promise_static_allSettledKeyed(JSContext* cx, unsigned argc, 3624 Value* vp) { 3625 JS_ReportErrorASCII(cx, "Promise.allSettledKeyed is not yet implemented"); 3626 return false; 3627 } 3628 #endif 3629 3630 [[nodiscard]] static bool PerformPromiseThen( 3631 JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled_, 3632 HandleValue onRejected_, Handle<PromiseCapability> resultCapability); 3633 3634 [[nodiscard]] static bool PerformPromiseThenWithoutSettleHandlers( 3635 JSContext* cx, Handle<PromiseObject*> promise, 3636 Handle<PromiseObject*> promiseToResolve, 3637 Handle<PromiseCapability> resultCapability); 3638 3639 static JSFunction* NewPromiseCombinatorElementFunction( 3640 JSContext* cx, Native native, 3641 Handle<PromiseCombinatorDataHolder*> dataHolder, uint32_t index, 3642 Handle<Value> maybeResolveFunc); 3643 3644 static bool PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, 3645 Value* vp); 3646 3647 /** 3648 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 3649 * 3650 * Promise.all ( iterable ) 3651 * https://tc39.es/ecma262/#sec-promise.all 3652 * PerformPromiseAll ( iteratorRecord, constructor, resultCapability, 3653 * promiseResolve ) 3654 * https://tc39.es/ecma262/#sec-performpromiseall 3655 * 3656 * Unforgeable version. 3657 */ 3658 [[nodiscard]] JSObject* js::GetWaitForAllPromise( 3659 JSContext* cx, JS::HandleObjectVector promises) { 3660 #ifdef DEBUG 3661 for (size_t i = 0, len = promises.length(); i < len; i++) { 3662 JSObject* obj = promises[i]; 3663 cx->check(obj); 3664 JSObject* unwrapped = UncheckedUnwrap(obj); 3665 MOZ_ASSERT(unwrapped->is<PromiseObject>() || JS_IsDeadWrapper(unwrapped)); 3666 } 3667 #endif 3668 3669 // Step 1. Let C be the this value. 3670 RootedObject C(cx, 3671 GlobalObject::getOrCreatePromiseConstructor(cx, cx->global())); 3672 if (!C) { 3673 return nullptr; 3674 } 3675 3676 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 3677 Rooted<PromiseCapability> resultCapability(cx); 3678 if (!NewPromiseCapability(cx, C, &resultCapability, false)) { 3679 return nullptr; 3680 } 3681 3682 // Steps 3-6 for iterator and iteratorRecord. 3683 // (omitted) 3684 3685 // Step 7. Let result be 3686 // PerformPromiseAll(iteratorRecord, C, promiseCapability, 3687 // promiseResolve). 3688 // 3689 // Implemented as an inlined, simplied version of PerformPromiseAll. 3690 { 3691 uint32_t promiseCount = promises.length(); 3692 // PerformPromiseAll 3693 3694 // Step 1. Let values be a new empty List. 3695 Rooted<PromiseCombinatorElements> values(cx); 3696 { 3697 auto* valuesArray = NewDenseFullyAllocatedArray(cx, promiseCount); 3698 if (!valuesArray) { 3699 return nullptr; 3700 } 3701 valuesArray->ensureDenseInitializedLength(0, promiseCount); 3702 3703 values.initialize(valuesArray); 3704 } 3705 3706 // Step 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. 3707 // 3708 // Create our data holder that holds all the things shared across 3709 // every step of the iterator. In particular, this holds the 3710 // remainingElementsCount (as an integer reserved slot), the array of 3711 // values, and the resolve function from our PromiseCapability. 3712 Rooted<PromiseCombinatorDataHolder*> dataHolder(cx); 3713 dataHolder = PromiseCombinatorDataHolder::New( 3714 cx, resultCapability.promise(), values, resultCapability.resolve()); 3715 if (!dataHolder) { 3716 return nullptr; 3717 } 3718 3719 // Call PerformPromiseThen with resolve and reject set to nullptr. 3720 Rooted<PromiseCapability> resultCapabilityWithoutResolving(cx); 3721 resultCapabilityWithoutResolving.promise().set(resultCapability.promise()); 3722 3723 // Step 3. Let index be 0. 3724 // Step 4. Repeat, 3725 // Step 4.t. Set index to index + 1. 3726 for (uint32_t index = 0; index < promiseCount; index++) { 3727 // Steps 4.a-c for IteratorStep. 3728 // (omitted) 3729 3730 // Step 4.d. (implemented after the loop). 3731 3732 // Steps 4.e-g for IteratorValue 3733 // (omitted) 3734 3735 // Step 4.h. Append undefined to values. 3736 values.unwrappedArray()->setDenseElement(index, UndefinedHandleValue); 3737 3738 // Step 4.i. Let nextPromise be 3739 // ? Call(promiseResolve, constructor, « nextValue »). 3740 RootedObject nextPromiseObj(cx, promises[index]); 3741 3742 // Steps 4.j-q. 3743 JSFunction* resolveFunc = NewPromiseCombinatorElementFunction( 3744 cx, PromiseAllResolveElementFunction, dataHolder, index, 3745 UndefinedHandleValue); 3746 if (!resolveFunc) { 3747 return nullptr; 3748 } 3749 3750 // Step 4.r. Set remainingElementsCount.[[Value]] to 3751 // remainingElementsCount.[[Value]] + 1. 3752 dataHolder->increaseRemainingCount(); 3753 3754 // Step 4.s. Perform 3755 // ? Invoke(nextPromise, "then", 3756 // « onFulfilled, resultCapability.[[Reject]] »). 3757 RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc)); 3758 RootedValue rejectFunVal(cx, ObjectValue(*resultCapability.reject())); 3759 Rooted<PromiseObject*> nextPromise(cx); 3760 3761 // GetWaitForAllPromise is used internally only and must not 3762 // trigger content-observable effects when registering a reaction. 3763 // It's also meant to work on wrapped Promises, potentially from 3764 // compartments with principals inaccessible from the current 3765 // compartment. To make that work, it unwraps promises with 3766 // UncheckedUnwrap, 3767 JSObject* unwrapped = UncheckedUnwrap(nextPromiseObj); 3768 if (JS_IsDeadWrapper(unwrapped)) { 3769 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 3770 JSMSG_DEAD_OBJECT); 3771 return nullptr; 3772 } 3773 nextPromise = &unwrapped->as<PromiseObject>(); 3774 3775 if (!PerformPromiseThen(cx, nextPromise, resolveFunVal, rejectFunVal, 3776 resultCapabilityWithoutResolving)) { 3777 return nullptr; 3778 } 3779 } 3780 3781 // Step 4.d.i. Set iteratorRecord.[[Done]] to true. 3782 // (implicit) 3783 3784 // Step 4.d.ii. Set remainingElementsCount.[[Value]] to 3785 // remainingElementsCount.[[Value]] - 1. 3786 int32_t remainingCount = dataHolder->decreaseRemainingCount(); 3787 3788 // Step 4.d.iii.If remainingElementsCount.[[Value]] is 0, then 3789 if (remainingCount == 0) { 3790 // Step 4.d.iii.1. Let valuesArray be ! CreateArrayFromList(values). 3791 // (already performed) 3792 3793 // Step 4.d.iii.2. Perform 3794 // ? Call(resultCapability.[[Resolve]], undefined, 3795 // « valuesArray »). 3796 if (!ResolvePromiseInternal(cx, resultCapability.promise(), 3797 values.value())) { 3798 return nullptr; 3799 } 3800 } 3801 } 3802 3803 // Step 4.d.iv. Return resultCapability.[[Promise]]. 3804 return resultCapability.promise(); 3805 } 3806 3807 static bool CallDefaultPromiseResolveFunction(JSContext* cx, 3808 Handle<PromiseObject*> promise, 3809 HandleValue resolutionValue); 3810 static bool CallDefaultPromiseRejectFunction( 3811 JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue, 3812 JS::Handle<SavedFrame*> unwrappedRejectionStack = nullptr); 3813 3814 /** 3815 * Perform Call(promiseCapability.[[Resolve]], undefined ,« value ») given 3816 * promiseCapability = { promiseObj, resolveFun }. 3817 * 3818 * Also, 3819 * 3820 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 3821 * 3822 * NewPromiseReactionJob ( reaction, argument ) 3823 * https://tc39.es/ecma262/#sec-newpromisereactionjob 3824 * 3825 * Steps 1.f-i. "type is Fulfill" case. 3826 */ 3827 [[nodiscard]] static bool CallPromiseResolveFunction(JSContext* cx, 3828 HandleObject resolveFun, 3829 HandleValue value, 3830 HandleObject promiseObj) { 3831 cx->check(resolveFun, value, promiseObj); 3832 3833 // NewPromiseReactionJob 3834 // Step 1.g. Assert: promiseCapability is a PromiseCapability Record. 3835 // (implicit) 3836 3837 if (resolveFun) { 3838 // NewPromiseReactionJob 3839 // Step 1.h. If handlerResult is an abrupt completion, then 3840 // (handled in CallPromiseRejectFunction) 3841 // Step 1.i. Else, 3842 // Step 1.i.i. Return 3843 // ? Call(promiseCapability.[[Resolve]], undefined, 3844 // « handlerResult.[[Value]] »). 3845 RootedValue calleeOrRval(cx, ObjectValue(*resolveFun)); 3846 return Call(cx, calleeOrRval, UndefinedHandleValue, value, &calleeOrRval); 3847 } 3848 3849 // `promiseObj` can be optimized away if it's known to be unused. 3850 // 3851 // NewPromiseReactionJob 3852 // Step f. If promiseCapability is undefined, then 3853 // (reordered) 3854 // 3855 // NOTE: "promiseCapability is undefined" case is represented by 3856 // `resolveFun == nullptr && promiseObj == nullptr`. 3857 if (!promiseObj) { 3858 // NewPromiseReactionJob 3859 // Step f.i. Assert: handlerResult is not an abrupt completion. 3860 // (implicit) 3861 3862 // Step f.ii. Return empty. 3863 return true; 3864 } 3865 3866 // NewPromiseReactionJob 3867 // Step 1.h. If handlerResult is an abrupt completion, then 3868 // (handled in CallPromiseRejectFunction) 3869 // Step 1.i. Else, 3870 // Step 1.i.i. Return 3871 // ? Call(promiseCapability.[[Resolve]], undefined, 3872 // « handlerResult.[[Value]] »). 3873 Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>(); 3874 if (IsPromiseWithDefaultResolvingFunction(promise)) { 3875 return CallDefaultPromiseResolveFunction(cx, promise, value); 3876 } 3877 3878 // This case is used by resultCapabilityWithoutResolving in 3879 // GetWaitForAllPromise, and nothing should be done. 3880 3881 return true; 3882 } 3883 3884 /** 3885 * Perform Call(promiseCapability.[[Reject]], undefined ,« reason ») given 3886 * promiseCapability = { promiseObj, rejectFun }. 3887 * 3888 * Also, 3889 * 3890 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 3891 * 3892 * NewPromiseReactionJob ( reaction, argument ) 3893 * https://tc39.es/ecma262/#sec-newpromisereactionjob 3894 * 3895 * Steps 1.g-i. "type is Reject" case. 3896 */ 3897 [[nodiscard]] static bool CallPromiseRejectFunction( 3898 JSContext* cx, HandleObject rejectFun, HandleValue reason, 3899 HandleObject promiseObj, Handle<SavedFrame*> unwrappedRejectionStack, 3900 UnhandledRejectionBehavior behavior) { 3901 cx->check(rejectFun, reason, promiseObj); 3902 3903 // NewPromiseReactionJob 3904 // Step 1.g. Assert: promiseCapability is a PromiseCapability Record. 3905 // (implicit) 3906 3907 if (rejectFun) { 3908 // NewPromiseReactionJob 3909 // Step 1.h. If handlerResult is an abrupt completion, then 3910 // Step 1.h.i. Return 3911 // ? Call(promiseCapability.[[Reject]], undefined, 3912 // « handlerResult.[[Value]] »). 3913 RootedValue calleeOrRval(cx, ObjectValue(*rejectFun)); 3914 return Call(cx, calleeOrRval, UndefinedHandleValue, reason, &calleeOrRval); 3915 } 3916 3917 // NewPromiseReactionJob 3918 // See the comment in CallPromiseResolveFunction for promiseCapability field 3919 // 3920 // Step f. If promiseCapability is undefined, then 3921 // Step f.i. Assert: handlerResult is not an abrupt completion. 3922 // 3923 // The spec doesn't allow promiseCapability to be undefined for reject case, 3924 // but `promiseObj` can be optimized away if it's known to be unused. 3925 if (!promiseObj) { 3926 if (behavior == UnhandledRejectionBehavior::Ignore) { 3927 // Do nothing if unhandled rejections are to be ignored. 3928 return true; 3929 } 3930 3931 // Otherwise create and reject a promise on the fly. The promise's 3932 // allocation time will be wrong. So it goes. 3933 Rooted<PromiseObject*> temporaryPromise( 3934 cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); 3935 if (!temporaryPromise) { 3936 cx->clearPendingException(); 3937 return true; 3938 } 3939 3940 // NewPromiseReactionJob 3941 // Step 1.h. If handlerResult is an abrupt completion, then 3942 // Step 1.h.i. Return 3943 // ? Call(promiseCapability.[[Reject]], undefined, 3944 // « handlerResult.[[Value]] »). 3945 return RejectPromiseInternal(cx, temporaryPromise, reason, 3946 unwrappedRejectionStack); 3947 } 3948 3949 // NewPromiseReactionJob 3950 // Step 1.h. If handlerResult is an abrupt completion, then 3951 // Step 1.h.i. Return 3952 // ? Call(promiseCapability.[[Reject]], undefined, 3953 // « handlerResult.[[Value]] »). 3954 Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>(); 3955 if (IsPromiseWithDefaultResolvingFunction(promise)) { 3956 return CallDefaultPromiseRejectFunction(cx, promise, reason, 3957 unwrappedRejectionStack); 3958 } 3959 3960 // This case is used by resultCapabilityWithoutResolving in 3961 // GetWaitForAllPromise, and nothing should be done. 3962 3963 return true; 3964 } 3965 3966 [[nodiscard]] static JSObject* CommonStaticResolveRejectImpl( 3967 JSContext* cx, HandleValue thisVal, HandleValue argVal, 3968 ResolutionMode mode); 3969 3970 static bool IsPromiseSpecies(JSContext* cx, JSFunction* species); 3971 3972 /** 3973 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 3974 * 3975 * Unified implementation of 3976 * 3977 * PerformPromiseAll ( iteratorRecord, constructor, resultCapability, 3978 * promiseResolve ) 3979 * https://tc39.es/ecma262/#sec-performpromiseall 3980 * PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, 3981 * promiseResolve ) 3982 * https://tc39.es/ecma262/#sec-performpromiseallsettled 3983 * PerformPromiseRace ( iteratorRecord, constructor, resultCapability, 3984 * promiseResolve ) 3985 * https://tc39.es/ecma262/#sec-performpromiserace 3986 * PerformPromiseAny ( iteratorRecord, constructor, resultCapability, 3987 * promiseResolve ) 3988 * https://tc39.es/ecma262/#sec-performpromiseany 3989 * 3990 * Promise.prototype.then ( onFulfilled, onRejected ) 3991 * https://tc39.es/ecma262/#sec-promise.prototype.then 3992 */ 3993 template <typename T> 3994 [[nodiscard]] static bool CommonPerformPromiseCombinator( 3995 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 3996 HandleObject resultPromise, HandleValue promiseResolve, bool* done, 3997 bool resolveReturnsUndefined, T getResolveAndReject) { 3998 RootedObject promiseCtor( 3999 cx, GlobalObject::getOrCreatePromiseConstructor(cx, cx->global())); 4000 if (!promiseCtor) { 4001 return false; 4002 } 4003 4004 // Optimized dense array iteration ensures no side-effects take place 4005 // during the iteration. 4006 bool iterationMayHaveSideEffects = !iterator.isOptimizedDenseArrayIteration(); 4007 4008 // Try to optimize when the Promise object is in its default state, guarded 4009 // by |C == promiseCtor| because we can only perform this optimization 4010 // for the builtin Promise constructor. 4011 bool isDefaultPromiseState = 4012 C == promiseCtor && HasDefaultPromiseProperties(cx); 4013 bool validatePromiseState = iterationMayHaveSideEffects; 4014 4015 RootedValue CVal(cx, ObjectValue(*C)); 4016 RootedValue resolveFunVal(cx); 4017 RootedValue rejectFunVal(cx); 4018 4019 // We're reusing rooted variables in the loop below, so we don't need to 4020 // declare a gazillion different rooted variables here. Rooted variables 4021 // which are reused include "Or" in their name. 4022 RootedValue nextValueOrNextPromise(cx); 4023 RootedObject nextPromiseObj(cx); 4024 RootedValue thenVal(cx); 4025 RootedObject thenSpeciesOrBlockedPromise(cx); 4026 Rooted<PromiseCapability> thenCapability(cx); 4027 4028 // PerformPromiseAll, PerformPromiseAllSettled, PerformPromiseAny 4029 // Step 4. 4030 // PerformPromiseRace 4031 // Step 1. 4032 while (true) { 4033 // Step a. Let next be ? IteratorStepValue(iteratorRecord). 4034 RootedValue& nextValue = nextValueOrNextPromise; 4035 if (!iterator.next(&nextValue, done)) { 4036 *done = true; 4037 return false; 4038 } 4039 4040 // Step b. If next is done, then 4041 if (*done) { 4042 return true; 4043 } 4044 4045 // Set to false when we can skip the [[Get]] for "then" and instead 4046 // use the built-in Promise.prototype.then function. 4047 bool getThen = true; 4048 4049 if (isDefaultPromiseState && validatePromiseState) { 4050 isDefaultPromiseState = HasDefaultPromiseProperties(cx); 4051 } 4052 4053 RootedValue& nextPromise = nextValueOrNextPromise; 4054 if (isDefaultPromiseState) { 4055 PromiseObject* nextValuePromise = nullptr; 4056 if (nextValue.isObject() && nextValue.toObject().is<PromiseObject>()) { 4057 nextValuePromise = &nextValue.toObject().as<PromiseObject>(); 4058 } 4059 4060 if (nextValuePromise && 4061 IsPromiseWithDefaultProperties(nextValuePromise, cx)) { 4062 // The below steps don't produce any side-effects, so we can 4063 // skip the Promise state revalidation in the next iteration 4064 // when the iterator itself also doesn't produce any 4065 // side-effects. 4066 validatePromiseState = iterationMayHaveSideEffects; 4067 4068 // Step {c, d}. Let nextPromise be 4069 // ? Call(promiseResolve, constructor, « next »). 4070 // Promise.resolve is a no-op for the default case. 4071 MOZ_ASSERT(&nextPromise.toObject() == nextValuePromise); 4072 4073 // `nextPromise` uses the built-in `then` function. 4074 getThen = false; 4075 } else { 4076 // Need to revalidate the Promise state in the next iteration, 4077 // because CommonStaticResolveRejectImpl may have modified it. 4078 validatePromiseState = true; 4079 4080 // Step {c, d}. Let nextPromise be 4081 // ? Call(promiseResolve, constructor, « next »). 4082 // Inline the call to Promise.resolve. 4083 JSObject* res = 4084 CommonStaticResolveRejectImpl(cx, CVal, nextValue, ResolveMode); 4085 if (!res) { 4086 return false; 4087 } 4088 4089 nextPromise.setObject(*res); 4090 } 4091 } else if (promiseResolve.isUndefined()) { 4092 // |promiseResolve| is undefined when the Promise constructor was 4093 // initially in its default state, i.e. if it had been retrieved, it would 4094 // have been set to |Promise.resolve|. 4095 4096 // Step {c, d}. Let nextPromise be 4097 // ? Call(promiseResolve, constructor, « next »). 4098 // Inline the call to Promise.resolve. 4099 JSObject* res = 4100 CommonStaticResolveRejectImpl(cx, CVal, nextValue, ResolveMode); 4101 if (!res) { 4102 return false; 4103 } 4104 4105 nextPromise.setObject(*res); 4106 } else { 4107 // Step {c, d}. Let nextPromise be 4108 // ? Call(promiseResolve, constructor, « next »). 4109 if (!Call(cx, promiseResolve, CVal, nextValue, &nextPromise)) { 4110 return false; 4111 } 4112 } 4113 4114 // Get the resolving functions for this iteration. 4115 // PerformPromiseAll 4116 // Steps c and e-m. 4117 // PerformPromiseAllSettled 4118 // Steps c and e-v. 4119 // PerformPromiseAny 4120 // Steps c and e-m. 4121 if (!getResolveAndReject(&resolveFunVal, &rejectFunVal)) { 4122 return false; 4123 } 4124 4125 // Call |nextPromise.then| with the provided hooks and add 4126 // |resultPromise| to the list of dependent promises. 4127 // 4128 // If |nextPromise.then| is the original |Promise.prototype.then| 4129 // function and the call to |nextPromise.then| would use the original 4130 // |Promise| constructor to create the resulting promise, we skip the 4131 // call to |nextPromise.then| and thus creating a new promise that 4132 // would not be observable by content. 4133 4134 // PerformPromiseAll 4135 // Step n. Perform 4136 // ? Invoke(nextPromise, "then", 4137 // « onFulfilled, resultCapability.[[Reject]] »). 4138 // PerformPromiseAllSettled 4139 // Step w. Perform 4140 // ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). 4141 // PerformPromiseRace 4142 // Step d. Perform 4143 // ? Invoke(nextPromise, "then", 4144 // « resultCapability.[[Resolve]], 4145 // resultCapability.[[Reject]] »). 4146 // PerformPromiseAny 4147 // Step n. Perform 4148 // ? Invoke(nextPromise, "then", 4149 // « resultCapability.[[Resolve]], onRejected »). 4150 nextPromiseObj = ToObject(cx, nextPromise); 4151 if (!nextPromiseObj) { 4152 return false; 4153 } 4154 4155 bool isBuiltinThen; 4156 bool isOnProto = false; 4157 bool isOnStandardProto = false; 4158 bool isOnObjectProto = false; 4159 if (getThen) { 4160 // We don't use the Promise lookup cache here, because this code 4161 // is only called when we had a lookup cache miss, so it's likely 4162 // we'd get another cache miss when trying to use the cache here. 4163 if (!GetThenValue(cx, nextPromiseObj, nextPromise, &thenVal, &isOnProto, 4164 &isOnStandardProto, &isOnObjectProto)) { 4165 return false; 4166 } 4167 4168 // |nextPromise| is an unwrapped Promise, and |then| is the 4169 // original |Promise.prototype.then|, inline it here. 4170 isBuiltinThen = nextPromiseObj->is<PromiseObject>() && 4171 IsNativeFunction(thenVal, Promise_then); 4172 } else { 4173 isBuiltinThen = true; 4174 } 4175 4176 // By default, the blocked promise is added as an extra entry to the 4177 // rejected promises list. 4178 bool addToDependent = true; 4179 4180 if (isBuiltinThen) { 4181 MOZ_ASSERT(nextPromise.isObject()); 4182 MOZ_ASSERT(&nextPromise.toObject() == nextPromiseObj); 4183 4184 // Promise.prototype.then 4185 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 4186 RootedObject& thenSpecies = thenSpeciesOrBlockedPromise; 4187 if (getThen) { 4188 thenSpecies = SpeciesConstructor(cx, nextPromiseObj, JSProto_Promise, 4189 IsPromiseSpecies); 4190 if (!thenSpecies) { 4191 return false; 4192 } 4193 } else { 4194 thenSpecies = promiseCtor; 4195 } 4196 4197 // The fast path here and the one in NewPromiseCapability may not 4198 // set the resolve and reject handlers, so we need to clear the 4199 // fields in case they were set in the previous iteration. 4200 thenCapability.resolve().set(nullptr); 4201 thenCapability.reject().set(nullptr); 4202 4203 // Skip the creation of a built-in Promise object if: 4204 // 1. `thenSpecies` is the built-in Promise constructor. 4205 // 2. `resolveFun` doesn't return an object, which ensures no side effects 4206 // occur in ResolvePromiseInternal. 4207 // 3. The result promise is a built-in Promise object. 4208 // 4. The result promise doesn't use the default resolving functions, 4209 // which in turn means Run{Fulfill,Reject}Function when called from 4210 // PromiseReactionJob won't try to resolve the promise. 4211 if (thenSpecies == promiseCtor && resolveReturnsUndefined && 4212 resultPromise->is<PromiseObject>() && 4213 !IsPromiseWithDefaultResolvingFunction( 4214 &resultPromise->as<PromiseObject>())) { 4215 thenCapability.promise().set(resultPromise); 4216 addToDependent = false; 4217 } else { 4218 // Promise.prototype.then 4219 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 4220 if (!NewPromiseCapability(cx, thenSpecies, &thenCapability, true)) { 4221 return false; 4222 } 4223 } 4224 4225 // Promise.prototype.then 4226 // Step 5. Return 4227 // PerformPromiseThen(promise, onFulfilled, onRejected, 4228 // resultCapability). 4229 Handle<PromiseObject*> promise = nextPromiseObj.as<PromiseObject>(); 4230 if (!PerformPromiseThen(cx, promise, resolveFunVal, rejectFunVal, 4231 thenCapability)) { 4232 return false; 4233 } 4234 } else { 4235 ReportThenable(cx, isOnProto, isOnStandardProto, isOnObjectProto); 4236 4237 RootedValue& ignored = thenVal; 4238 if (!Call(cx, thenVal, nextPromise, resolveFunVal, rejectFunVal, 4239 &ignored)) { 4240 return false; 4241 } 4242 4243 // In case the value to depend on isn't an object at all, there's 4244 // nothing more to do here: we can only add reactions to Promise 4245 // objects (potentially after unwrapping them), and non-object 4246 // values can't be Promise objects. This can happen if Promise.all 4247 // is called on an object with a `resolve` method that returns 4248 // primitives. 4249 if (!nextPromise.isObject()) { 4250 addToDependent = false; 4251 } 4252 } 4253 4254 // Adds |resultPromise| to the list of dependent promises. 4255 if (addToDependent) { 4256 // The object created by the |promise.then| call or the inlined 4257 // version of it above is visible to content (either because 4258 // |promise.then| was overridden by content and could leak it, 4259 // or because a constructor other than the original value of 4260 // |Promise| was used to create it). To have both that object and 4261 // |resultPromise| show up as dependent promises in the debugger, 4262 // add a dummy reaction to the list of reject reactions that 4263 // contains |resultPromise|, but otherwise does nothing. 4264 RootedObject& blockedPromise = thenSpeciesOrBlockedPromise; 4265 blockedPromise = resultPromise; 4266 4267 mozilla::Maybe<AutoRealm> ar; 4268 if (IsProxy(nextPromiseObj)) { 4269 nextPromiseObj = CheckedUnwrapStatic(nextPromiseObj); 4270 if (!nextPromiseObj) { 4271 ReportAccessDenied(cx); 4272 return false; 4273 } 4274 if (JS_IsDeadWrapper(nextPromiseObj)) { 4275 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 4276 JSMSG_DEAD_OBJECT); 4277 return false; 4278 } 4279 ar.emplace(cx, nextPromiseObj); 4280 if (!cx->compartment()->wrap(cx, &blockedPromise)) { 4281 return false; 4282 } 4283 } 4284 4285 // If either the object to depend on (`nextPromiseObj`) or the 4286 // object that gets blocked (`resultPromise`) isn't a, 4287 // maybe-wrapped, Promise instance, we ignore it. All this does is 4288 // lose some small amount of debug information in scenarios that 4289 // are highly unlikely to occur in useful code. 4290 if (nextPromiseObj->is<PromiseObject>() && 4291 resultPromise->is<PromiseObject>()) { 4292 Handle<PromiseObject*> promise = nextPromiseObj.as<PromiseObject>(); 4293 if (!AddDummyPromiseReactionForDebugger(cx, promise, blockedPromise)) { 4294 return false; 4295 } 4296 } 4297 } 4298 } 4299 } 4300 4301 // Create the elements for the Promise combinators Promise.all and 4302 // Promise.allSettled. 4303 // Create the errors list for the Promise combinator Promise.any. 4304 [[nodiscard]] static bool NewPromiseCombinatorElements( 4305 JSContext* cx, Handle<PromiseCapability> resultCapability, 4306 MutableHandle<PromiseCombinatorElements> elements) { 4307 // We have to be very careful about which compartments we create things for 4308 // the Promise combinators. In particular, we have to maintain the invariant 4309 // that anything stored in a reserved slot is same-compartment with the object 4310 // whose reserved slot it's in. But we want to create the values array in the 4311 // compartment of the result capability's Promise, because that array can get 4312 // exposed as the Promise's resolution value to code that has access to the 4313 // Promise (in particular code from that compartment), and that should work, 4314 // even if the Promise compartment is less-privileged than our caller 4315 // compartment. 4316 // 4317 // So the plan is as follows: Create the values array in the promise 4318 // compartment. Create the promise resolving functions and the data holder in 4319 // our current compartment, i.e. the compartment of the Promise combinator 4320 // function. Store a cross-compartment wrapper to the values array in the 4321 // holder. This should be OK because the only things we hand the promise 4322 // resolving functions to are the "then" calls we do and in the case when the 4323 // Promise's compartment is not the current compartment those are happening 4324 // over Xrays anyway, which means they get the canonical "then" function and 4325 // content can't see our promise resolving functions. 4326 4327 if (IsWrapper(resultCapability.promise())) { 4328 JSObject* unwrappedPromiseObj = 4329 CheckedUnwrapStatic(resultCapability.promise()); 4330 MOZ_ASSERT(unwrappedPromiseObj); 4331 4332 { 4333 AutoRealm ar(cx, unwrappedPromiseObj); 4334 auto* array = NewDenseEmptyArray(cx); 4335 if (!array) { 4336 return false; 4337 } 4338 elements.initialize(array); 4339 } 4340 4341 if (!cx->compartment()->wrap(cx, elements.value())) { 4342 return false; 4343 } 4344 } else { 4345 auto* array = NewDenseEmptyArray(cx); 4346 if (!array) { 4347 return false; 4348 } 4349 4350 elements.initialize(array); 4351 } 4352 return true; 4353 } 4354 4355 // Retrieve the combinator elements from the data holder. 4356 [[nodiscard]] static bool GetPromiseCombinatorElements( 4357 JSContext* cx, Handle<PromiseCombinatorDataHolder*> data, 4358 MutableHandle<PromiseCombinatorElements> elements) { 4359 bool needsWrapping = false; 4360 JSObject* valuesObj = &data->valuesArray().toObject(); 4361 if (IsProxy(valuesObj)) { 4362 // See comment for NewPromiseCombinatorElements for why we unwrap here. 4363 valuesObj = UncheckedUnwrap(valuesObj); 4364 4365 if (JS_IsDeadWrapper(valuesObj)) { 4366 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 4367 JSMSG_DEAD_OBJECT); 4368 return false; 4369 } 4370 4371 needsWrapping = true; 4372 } 4373 4374 elements.initialize(data, &valuesObj->as<ArrayObject>(), needsWrapping); 4375 return true; 4376 } 4377 4378 static JSFunction* NewPromiseCombinatorElementFunction( 4379 JSContext* cx, Native native, 4380 Handle<PromiseCombinatorDataHolder*> dataHolder, uint32_t index, 4381 Handle<Value> maybeResolveFunc) { 4382 JSFunction* fn = NewNativeFunction( 4383 cx, native, 1, nullptr, gc::AllocKind::FUNCTION_EXTENDED, GenericObject); 4384 if (!fn) { 4385 return nullptr; 4386 } 4387 4388 // See the PromiseCombinatorElementFunctionSlots comment for an explanation of 4389 // how these two slots are used. 4390 if (maybeResolveFunc.isObject()) { 4391 fn->setExtendedSlot( 4392 PromiseCombinatorElementFunctionSlot_ElementIndexOrResolveFunc, 4393 maybeResolveFunc); 4394 fn->setExtendedSlot(PromiseCombinatorElementFunctionSlot_Data, NullValue()); 4395 } else { 4396 fn->setExtendedSlot( 4397 PromiseCombinatorElementFunctionSlot_ElementIndexOrResolveFunc, 4398 Int32Value(index)); 4399 fn->setExtendedSlot(PromiseCombinatorElementFunctionSlot_Data, 4400 ObjectValue(*dataHolder)); 4401 } 4402 return fn; 4403 } 4404 4405 /** 4406 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4407 * 4408 * Unified implementation of 4409 * 4410 * Promise.all Resolve Element Functions 4411 * https://tc39.es/ecma262/#sec-promise.all-resolve-element-functions 4412 * 4413 * Steps 1-4. 4414 * 4415 * Promise.allSettled Resolve Element Functions 4416 * https://tc39.es/ecma262/#sec-promise.allsettled-resolve-element-functions 4417 * 4418 * Steps 1-5. 4419 * 4420 * Promise.allSettled Reject Element Functions 4421 * https://tc39.es/ecma262/#sec-promise.allsettled-reject-element-functions 4422 * 4423 * Steps 1-5. 4424 * 4425 * Promise.any Reject Element Functions 4426 * https://tc39.es/ecma262/#sec-promise.any-reject-element-functions 4427 * 4428 * Steps 1-4. 4429 * 4430 * Common implementation for Promise combinator element functions to check if 4431 * they've already been called. 4432 */ 4433 static bool PromiseCombinatorElementFunctionAlreadyCalled( 4434 const CallArgs& args, MutableHandle<PromiseCombinatorDataHolder*> data, 4435 uint32_t* index) { 4436 // Step 1. Let F be the active function object. 4437 JSFunction* fn = &args.callee().as<JSFunction>(); 4438 4439 // Promise.{all,any} functions 4440 // Step 2. If F.[[AlreadyCalled]] is true, return undefined. 4441 // Promise.allSettled functions 4442 // Step 2. Let alreadyCalled be F.[[AlreadyCalled]]. 4443 // Step 3. If alreadyCalled.[[Value]] is true, return undefined. 4444 // 4445 // We use the existence of the data holder as a signal for whether the Promise 4446 // combinator element function was already called. Upon resolution, it's reset 4447 // to `undefined`. 4448 // 4449 // For Promise.allSettled, the [[AlreadyCalled]] state must be shared by the 4450 // two functions, so we always use the resolve function's state. 4451 4452 constexpr size_t indexOrResolveFuncSlot = 4453 PromiseCombinatorElementFunctionSlot_ElementIndexOrResolveFunc; 4454 if (fn->getExtendedSlot(indexOrResolveFuncSlot).isObject()) { 4455 // This is a reject function for Promise.allSettled. Get the corresponding 4456 // resolve function. 4457 Value slotVal = fn->getExtendedSlot(indexOrResolveFuncSlot); 4458 fn = &slotVal.toObject().as<JSFunction>(); 4459 } 4460 MOZ_RELEASE_ASSERT(fn->getExtendedSlot(indexOrResolveFuncSlot).isInt32()); 4461 4462 const Value& dataVal = 4463 fn->getExtendedSlot(PromiseCombinatorElementFunctionSlot_Data); 4464 if (dataVal.isUndefined()) { 4465 return true; 4466 } 4467 4468 data.set(&dataVal.toObject().as<PromiseCombinatorDataHolder>()); 4469 4470 // Promise.{all,any} functions 4471 // Step 3. Set F.[[AlreadyCalled]] to true. 4472 // Promise.allSettled functions 4473 // Step 4. Set alreadyCalled.[[Value]] to true. 4474 fn->setExtendedSlot(PromiseCombinatorElementFunctionSlot_Data, 4475 UndefinedValue()); 4476 4477 // Promise.{all,any} functions 4478 // Step 4. Let index be F.[[Index]]. 4479 // Promise.allSettled functions 4480 // Step 5. Let index be F.[[Index]]. 4481 int32_t idx = fn->getExtendedSlot(indexOrResolveFuncSlot).toInt32(); 4482 MOZ_ASSERT(idx >= 0); 4483 *index = uint32_t(idx); 4484 4485 return false; 4486 } 4487 4488 /** 4489 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4490 * 4491 * PerformPromiseAll ( iteratorRecord, constructor, resultCapability, 4492 * promiseResolve ) 4493 * https://tc39.es/ecma262/#sec-performpromiseall 4494 */ 4495 [[nodiscard]] static bool PerformPromiseAll( 4496 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 4497 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 4498 bool* done) { 4499 *done = false; 4500 4501 MOZ_ASSERT(C->isConstructor()); 4502 4503 // Step 1. Let values be a new empty List. 4504 Rooted<PromiseCombinatorElements> values(cx); 4505 if (!NewPromiseCombinatorElements(cx, resultCapability, &values)) { 4506 return false; 4507 } 4508 4509 // Step 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. 4510 // 4511 // Create our data holder that holds all the things shared across 4512 // every step of the iterator. In particular, this holds the 4513 // remainingElementsCount (as an integer reserved slot), the array of 4514 // values, and the resolve function from our PromiseCapability. 4515 Rooted<PromiseCombinatorDataHolder*> dataHolder(cx); 4516 dataHolder = PromiseCombinatorDataHolder::New( 4517 cx, resultCapability.promise(), values, resultCapability.resolve()); 4518 if (!dataHolder) { 4519 return false; 4520 } 4521 4522 // Step 3. Let index be 0. 4523 uint32_t index = 0; 4524 4525 auto getResolveAndReject = [cx, &resultCapability, &values, &dataHolder, 4526 &index](MutableHandleValue resolveFunVal, 4527 MutableHandleValue rejectFunVal) { 4528 // Step 4.c. Append undefined to values. 4529 if (!values.pushUndefined(cx)) { 4530 return false; 4531 } 4532 4533 // Steps 4.e-l. 4534 JSFunction* resolveFunc = NewPromiseCombinatorElementFunction( 4535 cx, PromiseAllResolveElementFunction, dataHolder, index, 4536 UndefinedHandleValue); 4537 if (!resolveFunc) { 4538 return false; 4539 } 4540 4541 // Step 4.m. Set remainingElementsCount.[[Value]] to 4542 // remainingElementsCount.[[Value]] + 1. 4543 dataHolder->increaseRemainingCount(); 4544 4545 // Step 4.o. Set index to index + 1. 4546 index++; 4547 MOZ_ASSERT(index > 0); 4548 4549 resolveFunVal.setObject(*resolveFunc); 4550 rejectFunVal.setObject(*resultCapability.reject()); 4551 return true; 4552 }; 4553 4554 // Step 4. Repeat, 4555 if (!CommonPerformPromiseCombinator( 4556 cx, iterator, C, resultCapability.promise(), promiseResolve, done, 4557 true, getResolveAndReject)) { 4558 return false; 4559 } 4560 4561 // Step 4.b.i. Set remainingElementsCount.[[Value]] to 4562 // remainingElementsCount.[[Value]] - 1. 4563 int32_t remainingCount = dataHolder->decreaseRemainingCount(); 4564 4565 // Step 4.b.ii. If remainingElementsCount.[[Value]] is 0, then 4566 if (remainingCount == 0) { 4567 // Step 4.b.ii.1. Let valuesArray be CreateArrayFromList(values). 4568 // (already performed) 4569 4570 // Step 4.b.ii.2. Perform 4571 // ? Call(resultCapability.[[Resolve]], undefined, 4572 // « valuesArray »). 4573 return CallPromiseResolveFunction(cx, resultCapability.resolve(), 4574 values.value(), 4575 resultCapability.promise()); 4576 } 4577 4578 // Step 4.d.iii. Return resultCapability.[[Promise]]. 4579 return true; 4580 } 4581 4582 /** 4583 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4584 * 4585 * Promise.all Resolve Element Functions 4586 * https://tc39.es/ecma262/#sec-promise.all-resolve-element-functions 4587 */ 4588 static bool PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, 4589 Value* vp) { 4590 CallArgs args = CallArgsFromVp(argc, vp); 4591 HandleValue xVal = args.get(0); 4592 4593 // Steps 1-4. 4594 Rooted<PromiseCombinatorDataHolder*> data(cx); 4595 uint32_t index; 4596 if (PromiseCombinatorElementFunctionAlreadyCalled(args, &data, &index)) { 4597 args.rval().setUndefined(); 4598 return true; 4599 } 4600 4601 // Step 5. Let values be F.[[Values]]. 4602 Rooted<PromiseCombinatorElements> values(cx); 4603 if (!GetPromiseCombinatorElements(cx, data, &values)) { 4604 return false; 4605 } 4606 4607 // Step 8. Set values[index] to x. 4608 if (!values.setElement(cx, index, xVal)) { 4609 return false; 4610 } 4611 4612 // (reordered) 4613 // Step 7. Let remainingElementsCount be F.[[RemainingElements]]. 4614 // 4615 // Step 9. Set remainingElementsCount.[[Value]] to 4616 // remainingElementsCount.[[Value]] - 1. 4617 uint32_t remainingCount = data->decreaseRemainingCount(); 4618 4619 // Step 10. If remainingElementsCount.[[Value]] is 0, then 4620 if (remainingCount == 0) { 4621 // Step 10.a. Let valuesArray be CreateArrayFromList(values). 4622 // (already performed) 4623 4624 // (reordered) 4625 // Step 6. Let promiseCapability be F.[[Capability]]. 4626 // 4627 // Step 10.b. Return 4628 // ? Call(promiseCapability.[[Resolve]], undefined, 4629 // « valuesArray »). 4630 RootedObject resolveAllFun(cx, data->resolveOrRejectObj()); 4631 RootedObject promiseObj(cx, data->promiseObj()); 4632 if (!CallPromiseResolveFunction(cx, resolveAllFun, values.value(), 4633 promiseObj)) { 4634 return false; 4635 } 4636 } 4637 4638 // Step 11. Return undefined. 4639 args.rval().setUndefined(); 4640 return true; 4641 } 4642 4643 /** 4644 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4645 * 4646 * Promise.race ( iterable ) 4647 * https://tc39.es/ecma262/#sec-promise.race 4648 */ 4649 static bool Promise_static_race(JSContext* cx, unsigned argc, Value* vp) { 4650 CallArgs args = CallArgsFromVp(argc, vp); 4651 return CommonPromiseCombinator(cx, args, CombinatorKind::Race); 4652 } 4653 4654 /** 4655 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4656 * 4657 * PerformPromiseRace ( iteratorRecord, constructor, resultCapability, 4658 * promiseResolve ) 4659 * https://tc39.es/ecma262/#sec-performpromiserace 4660 */ 4661 [[nodiscard]] static bool PerformPromiseRace( 4662 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 4663 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 4664 bool* done) { 4665 *done = false; 4666 4667 MOZ_ASSERT(C->isConstructor()); 4668 4669 // BlockOnPromise fast path requires the passed onFulfilled function 4670 // doesn't return an object value, because otherwise the skipped promise 4671 // creation is detectable due to missing property lookups. 4672 bool isDefaultResolveFn = 4673 IsNativeFunction(resultCapability.resolve(), ResolvePromiseFunction); 4674 4675 auto getResolveAndReject = [&resultCapability]( 4676 MutableHandleValue resolveFunVal, 4677 MutableHandleValue rejectFunVal) { 4678 resolveFunVal.setObject(*resultCapability.resolve()); 4679 rejectFunVal.setObject(*resultCapability.reject()); 4680 return true; 4681 }; 4682 4683 // Step 1. Repeat, 4684 return CommonPerformPromiseCombinator( 4685 cx, iterator, C, resultCapability.promise(), promiseResolve, done, 4686 isDefaultResolveFn, getResolveAndReject); 4687 } 4688 4689 enum class PromiseAllSettledElementFunctionKind { Resolve, Reject }; 4690 4691 template <PromiseAllSettledElementFunctionKind Kind> 4692 static bool PromiseAllSettledElementFunction(JSContext* cx, unsigned argc, 4693 Value* vp); 4694 4695 /** 4696 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4697 * 4698 * Promise.allSettled ( iterable ) 4699 * https://tc39.es/ecma262/#sec-promise.allsettled 4700 */ 4701 static bool Promise_static_allSettled(JSContext* cx, unsigned argc, Value* vp) { 4702 CallArgs args = CallArgsFromVp(argc, vp); 4703 return CommonPromiseCombinator(cx, args, CombinatorKind::AllSettled); 4704 } 4705 4706 /** 4707 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4708 * 4709 * PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, 4710 * promiseResolve ) 4711 * https://tc39.es/ecma262/#sec-performpromiseallsettled 4712 */ 4713 [[nodiscard]] static bool PerformPromiseAllSettled( 4714 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 4715 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 4716 bool* done) { 4717 *done = false; 4718 4719 MOZ_ASSERT(C->isConstructor()); 4720 4721 // Step 1. Let values be a new empty List. 4722 Rooted<PromiseCombinatorElements> values(cx); 4723 if (!NewPromiseCombinatorElements(cx, resultCapability, &values)) { 4724 return false; 4725 } 4726 4727 // Step 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. 4728 // 4729 // Create our data holder that holds all the things shared across every step 4730 // of the iterator. In particular, this holds the remainingElementsCount 4731 // (as an integer reserved slot), the array of values, and the resolve 4732 // function from our PromiseCapability. 4733 Rooted<PromiseCombinatorDataHolder*> dataHolder(cx); 4734 dataHolder = PromiseCombinatorDataHolder::New( 4735 cx, resultCapability.promise(), values, resultCapability.resolve()); 4736 if (!dataHolder) { 4737 return false; 4738 } 4739 4740 // Step 3. Let index be 0. 4741 uint32_t index = 0; 4742 4743 auto getResolveAndReject = [cx, &values, &dataHolder, &index]( 4744 MutableHandleValue resolveFunVal, 4745 MutableHandleValue rejectFunVal) { 4746 // Step 4.c. Append undefined to values. 4747 if (!values.pushUndefined(cx)) { 4748 return false; 4749 } 4750 4751 auto PromiseAllSettledResolveElementFunction = 4752 PromiseAllSettledElementFunction< 4753 PromiseAllSettledElementFunctionKind::Resolve>; 4754 auto PromiseAllSettledRejectElementFunction = 4755 PromiseAllSettledElementFunction< 4756 PromiseAllSettledElementFunctionKind::Reject>; 4757 4758 // Steps 4.e-m. 4759 JSFunction* resolveFunc = NewPromiseCombinatorElementFunction( 4760 cx, PromiseAllSettledResolveElementFunction, dataHolder, index, 4761 UndefinedHandleValue); 4762 if (!resolveFunc) { 4763 return false; 4764 } 4765 resolveFunVal.setObject(*resolveFunc); 4766 4767 // Steps 4.n-u. 4768 JSFunction* rejectFunc = NewPromiseCombinatorElementFunction( 4769 cx, PromiseAllSettledRejectElementFunction, dataHolder, index, 4770 resolveFunVal); 4771 if (!rejectFunc) { 4772 return false; 4773 } 4774 rejectFunVal.setObject(*rejectFunc); 4775 4776 // Step 4.v. Set remainingElementsCount.[[Value]] to 4777 // remainingElementsCount.[[Value]] + 1. 4778 dataHolder->increaseRemainingCount(); 4779 4780 // Step 4.x. Set index to index + 1. 4781 index++; 4782 MOZ_ASSERT(index > 0); 4783 4784 return true; 4785 }; 4786 4787 // Step 4. Repeat, 4788 if (!CommonPerformPromiseCombinator( 4789 cx, iterator, C, resultCapability.promise(), promiseResolve, done, 4790 true, getResolveAndReject)) { 4791 return false; 4792 } 4793 4794 // Step 4.b.ii. Set remainingElementsCount.[[Value]] to 4795 // remainingElementsCount.[[Value]] - 1. 4796 int32_t remainingCount = dataHolder->decreaseRemainingCount(); 4797 4798 // Step 4.b.ii. If remainingElementsCount.[[Value]] is 0, then 4799 if (remainingCount == 0) { 4800 // Step 4.b.ii.1. Let valuesArray be CreateArrayFromList(values). 4801 // (already performed) 4802 4803 // Step 4.b.ii.2. Perform 4804 // ? Call(resultCapability.[[Resolve]], undefined, 4805 // « valuesArray »). 4806 return CallPromiseResolveFunction(cx, resultCapability.resolve(), 4807 values.value(), 4808 resultCapability.promise()); 4809 } 4810 4811 return true; 4812 } 4813 4814 /** 4815 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4816 * 4817 * Unified implementation of 4818 * 4819 * Promise.allSettled Resolve Element Functions 4820 * https://tc39.es/ecma262/#sec-promise.allsettled-resolve-element-functions 4821 * Promise.allSettled Reject Element Functions 4822 * https://tc39.es/ecma262/#sec-promise.allsettled-reject-element-functions 4823 */ 4824 template <PromiseAllSettledElementFunctionKind Kind> 4825 static bool PromiseAllSettledElementFunction(JSContext* cx, unsigned argc, 4826 Value* vp) { 4827 CallArgs args = CallArgsFromVp(argc, vp); 4828 HandleValue valueOrReason = args.get(0); 4829 4830 // Steps 1-5. 4831 Rooted<PromiseCombinatorDataHolder*> data(cx); 4832 uint32_t index; 4833 if (PromiseCombinatorElementFunctionAlreadyCalled(args, &data, &index)) { 4834 args.rval().setUndefined(); 4835 return true; 4836 } 4837 4838 // Step 6. Let values be F.[[Values]]. 4839 Rooted<PromiseCombinatorElements> values(cx); 4840 if (!GetPromiseCombinatorElements(cx, data, &values)) { 4841 return false; 4842 } 4843 4844 // Step 9. Let obj be OrdinaryObjectCreate(%Object.prototype%). 4845 Rooted<PlainObject*> obj(cx, NewPlainObject(cx)); 4846 if (!obj) { 4847 return false; 4848 } 4849 4850 // Promise.allSettled Resolve Element Functions 4851 // Step 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). 4852 // Promise.allSettled Reject Element Functions 4853 // Step 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). 4854 RootedId id(cx, NameToId(cx->names().status)); 4855 RootedValue statusValue(cx); 4856 if (Kind == PromiseAllSettledElementFunctionKind::Resolve) { 4857 statusValue.setString(cx->names().fulfilled); 4858 } else { 4859 statusValue.setString(cx->names().rejected); 4860 } 4861 if (!NativeDefineDataProperty(cx, obj, id, statusValue, JSPROP_ENUMERATE)) { 4862 return false; 4863 } 4864 4865 // Promise.allSettled Resolve Element Functions 4866 // Step 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). 4867 // Promise.allSettled Reject Element Functions 4868 // Step 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). 4869 if (Kind == PromiseAllSettledElementFunctionKind::Resolve) { 4870 id = NameToId(cx->names().value); 4871 } else { 4872 id = NameToId(cx->names().reason); 4873 } 4874 if (!NativeDefineDataProperty(cx, obj, id, valueOrReason, JSPROP_ENUMERATE)) { 4875 return false; 4876 } 4877 4878 // Step 12. Set values[index] to obj. 4879 RootedValue objVal(cx, ObjectValue(*obj)); 4880 if (!values.setElement(cx, index, objVal)) { 4881 return false; 4882 } 4883 4884 // (reordered) 4885 // Step 8. Let remainingElementsCount be F.[[RemainingElements]]. 4886 // 4887 // Step 13. Set remainingElementsCount.[[Value]] to 4888 // remainingElementsCount.[[Value]] - 1. 4889 uint32_t remainingCount = data->decreaseRemainingCount(); 4890 4891 // Step 14. If remainingElementsCount.[[Value]] is 0, then 4892 if (remainingCount == 0) { 4893 // Step 14.a. Let valuesArray be ! CreateArrayFromList(values). 4894 // (already performed) 4895 4896 // (reordered) 4897 // Step 7. Let promiseCapability be F.[[Capability]]. 4898 // 4899 // Step 14.b. Return 4900 // ? Call(promiseCapability.[[Resolve]], undefined, 4901 // « valuesArray »). 4902 RootedObject resolveAllFun(cx, data->resolveOrRejectObj()); 4903 RootedObject promiseObj(cx, data->promiseObj()); 4904 if (!CallPromiseResolveFunction(cx, resolveAllFun, values.value(), 4905 promiseObj)) { 4906 return false; 4907 } 4908 } 4909 4910 // Step 15. Return undefined. 4911 args.rval().setUndefined(); 4912 return true; 4913 } 4914 4915 /** 4916 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4917 * 4918 * Promise.any ( iterable ) 4919 * https://tc39.es/ecma262/#sec-promise.any 4920 */ 4921 static bool Promise_static_any(JSContext* cx, unsigned argc, Value* vp) { 4922 CallArgs args = CallArgsFromVp(argc, vp); 4923 return CommonPromiseCombinator(cx, args, CombinatorKind::Any); 4924 } 4925 4926 static bool PromiseAnyRejectElementFunction(JSContext* cx, unsigned argc, 4927 Value* vp); 4928 4929 static void ThrowAggregateError(JSContext* cx, 4930 Handle<PromiseCombinatorElements> errors, 4931 HandleObject promise); 4932 4933 /** 4934 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 4935 * 4936 * Promise.any ( iterable ) 4937 * https://tc39.es/ecma262/#sec-promise.any 4938 * PerformPromiseAny ( iteratorRecord, constructor, resultCapability, 4939 * promiseResolve ) 4940 * https://tc39.es/ecma262/#sec-performpromiseany 4941 */ 4942 [[nodiscard]] static bool PerformPromiseAny( 4943 JSContext* cx, PromiseForOfIterator& iterator, HandleObject C, 4944 Handle<PromiseCapability> resultCapability, HandleValue promiseResolve, 4945 bool* done) { 4946 MOZ_ASSERT(C->isConstructor()); 4947 4948 *done = false; 4949 4950 // Step 1. Let errors be a new empty List. 4951 Rooted<PromiseCombinatorElements> errors(cx); 4952 if (!NewPromiseCombinatorElements(cx, resultCapability, &errors)) { 4953 return false; 4954 } 4955 4956 // Step 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. 4957 // 4958 // Create our data holder that holds all the things shared across every step 4959 // of the iterator. In particular, this holds the remainingElementsCount (as 4960 // an integer reserved slot), the array of errors, and the reject function 4961 // from our PromiseCapability. 4962 Rooted<PromiseCombinatorDataHolder*> dataHolder(cx); 4963 dataHolder = PromiseCombinatorDataHolder::New( 4964 cx, resultCapability.promise(), errors, resultCapability.reject()); 4965 if (!dataHolder) { 4966 return false; 4967 } 4968 4969 // Step 3. Let index be 0. 4970 uint32_t index = 0; 4971 4972 auto getResolveAndReject = [cx, &resultCapability, &errors, &dataHolder, 4973 &index](MutableHandleValue resolveFunVal, 4974 MutableHandleValue rejectFunVal) { 4975 // Step 4.c. Append undefined to errors. 4976 if (!errors.pushUndefined(cx)) { 4977 return false; 4978 } 4979 4980 // Steps 4.e-l. 4981 JSFunction* rejectFunc = NewPromiseCombinatorElementFunction( 4982 cx, PromiseAnyRejectElementFunction, dataHolder, index, 4983 UndefinedHandleValue); 4984 if (!rejectFunc) { 4985 return false; 4986 } 4987 4988 // Step 4.m. Set remainingElementsCount.[[Value]] to 4989 // remainingElementsCount.[[Value]] + 1. 4990 dataHolder->increaseRemainingCount(); 4991 4992 // Step 4.o. Set index to index + 1. 4993 index++; 4994 MOZ_ASSERT(index > 0); 4995 4996 resolveFunVal.setObject(*resultCapability.resolve()); 4997 rejectFunVal.setObject(*rejectFunc); 4998 return true; 4999 }; 5000 5001 // BlockOnPromise fast path requires the passed onFulfilled function doesn't 5002 // return an object value, because otherwise the skipped promise creation is 5003 // detectable due to missing property lookups. 5004 bool isDefaultResolveFn = 5005 IsNativeFunction(resultCapability.resolve(), ResolvePromiseFunction); 5006 5007 // Step 4. Repeat, 5008 if (!CommonPerformPromiseCombinator( 5009 cx, iterator, C, resultCapability.promise(), promiseResolve, done, 5010 isDefaultResolveFn, getResolveAndReject)) { 5011 return false; 5012 } 5013 5014 // Step 4.b.i. Set remainingElementsCount.[[Value]] to 5015 // remainingElementsCount.[[Value]] - 1.. 5016 int32_t remainingCount = dataHolder->decreaseRemainingCount(); 5017 5018 // Step 4.b.ii. If remainingElementsCount.[[Value]] = 0, then 5019 if (remainingCount == 0) { 5020 ThrowAggregateError(cx, errors, resultCapability.promise()); 5021 return false; 5022 } 5023 5024 // Step 4.b.iii. Return resultCapability.[[Promise]]. 5025 return true; 5026 } 5027 5028 /** 5029 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 5030 * 5031 * Promise.any Reject Element Functions 5032 * https://tc39.es/ecma262/#sec-promise.any-reject-element-functions 5033 */ 5034 static bool PromiseAnyRejectElementFunction(JSContext* cx, unsigned argc, 5035 Value* vp) { 5036 CallArgs args = CallArgsFromVp(argc, vp); 5037 HandleValue xVal = args.get(0); 5038 5039 // Steps 1-4. 5040 Rooted<PromiseCombinatorDataHolder*> data(cx); 5041 uint32_t index; 5042 if (PromiseCombinatorElementFunctionAlreadyCalled(args, &data, &index)) { 5043 args.rval().setUndefined(); 5044 return true; 5045 } 5046 5047 // Step 5. 5048 Rooted<PromiseCombinatorElements> errors(cx); 5049 if (!GetPromiseCombinatorElements(cx, data, &errors)) { 5050 return false; 5051 } 5052 5053 // Step 8. 5054 if (!errors.setElement(cx, index, xVal)) { 5055 return false; 5056 } 5057 5058 // Steps 7 and 9. 5059 uint32_t remainingCount = data->decreaseRemainingCount(); 5060 5061 // Step 10. 5062 if (remainingCount == 0) { 5063 // Step 6 (Adapted to work with PromiseCombinatorDataHolder's layout). 5064 RootedObject rejectFun(cx, data->resolveOrRejectObj()); 5065 RootedObject promiseObj(cx, data->promiseObj()); 5066 5067 // Steps 10.a-b. 5068 ThrowAggregateError(cx, errors, promiseObj); 5069 5070 RootedValue reason(cx); 5071 Rooted<SavedFrame*> stack(cx); 5072 if (!MaybeGetAndClearExceptionAndStack(cx, &reason, &stack)) { 5073 return false; 5074 } 5075 5076 // Step 10.c. 5077 if (!CallPromiseRejectFunction(cx, rejectFun, reason, promiseObj, stack, 5078 UnhandledRejectionBehavior::Report)) { 5079 return false; 5080 } 5081 } 5082 5083 // Step 11. 5084 args.rval().setUndefined(); 5085 return true; 5086 } 5087 5088 /** 5089 * ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec 5090 * 5091 * PerformPromiseAny ( iteratorRecord, constructor, resultCapability, 5092 * promiseResolve ) 5093 * https://tc39.es/ecma262/#sec-performpromiseany 5094 * 5095 * Steps 4.b.ii.1-3 5096 */ 5097 static void ThrowAggregateError(JSContext* cx, 5098 Handle<PromiseCombinatorElements> errors, 5099 HandleObject promise) { 5100 MOZ_ASSERT(!cx->isExceptionPending()); 5101 5102 // Create the AggregateError in the same realm as the array object. 5103 AutoRealm ar(cx, errors.unwrappedArray()); 5104 5105 RootedObject allocationSite(cx); 5106 mozilla::Maybe<JS::AutoSetAsyncStackForNewCalls> asyncStack; 5107 5108 // Provide a more useful error stack if possible: This function is typically 5109 // called from Promise job queue, which doesn't have any JS frames on the 5110 // stack. So when we create the AggregateError below, its stack property will 5111 // be set to the empty string, which makes it harder to debug the error cause. 5112 // To avoid this situation set-up an async stack based on the Promise 5113 // allocation site, which should point to calling site of |Promise.any|. 5114 if (promise->is<PromiseObject>()) { 5115 allocationSite = promise->as<PromiseObject>().allocationSite(); 5116 if (allocationSite) { 5117 asyncStack.emplace( 5118 cx, allocationSite, "Promise.any", 5119 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT); 5120 } 5121 } 5122 5123 // Step 4.b.ii.1. Let error be a newly created AggregateError object. 5124 // 5125 // AutoSetAsyncStackForNewCalls requires a new activation before it takes 5126 // effect, so call into the self-hosting helper to set-up new call frames. 5127 RootedValue error(cx); 5128 if (!GetAggregateError(cx, JSMSG_PROMISE_ANY_REJECTION, &error)) { 5129 return; 5130 } 5131 5132 // Step 4.b.ii.2. Perform ! DefinePropertyOrThrow( 5133 // error, "errors", PropertyDescriptor { 5134 // [[Configurable]]: true, [[Enumerable]]: false, 5135 // [[Writable]]: true, 5136 // [[Value]]: ! CreateArrayFromList(errors) }). 5137 // 5138 // |error| isn't guaranteed to be an AggregateError in case of OOM or stack 5139 // overflow. 5140 Rooted<SavedFrame*> stack(cx); 5141 if (error.isObject() && error.toObject().is<ErrorObject>()) { 5142 Rooted<ErrorObject*> errorObj(cx, &error.toObject().as<ErrorObject>()); 5143 if (errorObj->type() == JSEXN_AGGREGATEERR) { 5144 RootedValue errorsVal(cx, JS::ObjectValue(*errors.unwrappedArray())); 5145 if (!NativeDefineDataProperty(cx, errorObj, cx->names().errors, errorsVal, 5146 0)) { 5147 return; 5148 } 5149 5150 // Adopt the existing saved frames when present. 5151 if (JSObject* errorStack = errorObj->stack()) { 5152 stack = &errorStack->as<SavedFrame>(); 5153 } 5154 } 5155 } 5156 5157 // Step 4.b.ii.3. Return ThrowCompletion(error). 5158 cx->setPendingException(error, stack); 5159 } 5160 5161 /** 5162 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5163 * 5164 * Unified implementation of 5165 * 5166 * Promise.reject ( r ) 5167 * https://tc39.es/ecma262/#sec-promise.reject 5168 * NewPromiseCapability ( C ) 5169 * https://tc39.es/ecma262/#sec-newpromisecapability 5170 * Promise.resolve ( x ) 5171 * https://tc39.es/ecma262/#sec-promise.resolve 5172 * PromiseResolve ( C, x ) 5173 * https://tc39.es/ecma262/#sec-promise-resolve 5174 */ 5175 [[nodiscard]] static JSObject* CommonStaticResolveRejectImpl( 5176 JSContext* cx, HandleValue thisVal, HandleValue argVal, 5177 ResolutionMode mode) { 5178 // Promise.reject 5179 // Step 1. Let C be the this value. 5180 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 5181 // 5182 // Promise.reject => NewPromiseCapability 5183 // Step 1. If IsConstructor(C) is false, throw a TypeError exception. 5184 // 5185 // Promise.resolve 5186 // Step 1. Let C be the this value. 5187 // Step 2. If Type(C) is not Object, throw a TypeError exception. 5188 if (!thisVal.isObject()) { 5189 const char* msg = mode == ResolveMode ? "Receiver of Promise.resolve call" 5190 : "Receiver of Promise.reject call"; 5191 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5192 JSMSG_OBJECT_REQUIRED, msg); 5193 return nullptr; 5194 } 5195 RootedObject C(cx, &thisVal.toObject()); 5196 5197 // Promise.resolve 5198 // Step 3. Return ? PromiseResolve(C, x). 5199 // 5200 // PromiseResolve 5201 // Step 1. Assert: Type(C) is Object. 5202 // (implicit) 5203 if (mode == ResolveMode && argVal.isObject()) { 5204 RootedObject xObj(cx, &argVal.toObject()); 5205 bool isPromise = false; 5206 if (xObj->is<PromiseObject>()) { 5207 isPromise = true; 5208 } else if (IsWrapper(xObj)) { 5209 // Treat instances of Promise from other compartments as Promises 5210 // here, too. 5211 // It's important to do the GetProperty for the `constructor` 5212 // below through the wrapper, because wrappers can change the 5213 // outcome, so instead of unwrapping and then performing the 5214 // GetProperty, just check here and then operate on the original 5215 // object again. 5216 if (xObj->canUnwrapAs<PromiseObject>()) { 5217 isPromise = true; 5218 } 5219 } 5220 5221 // PromiseResolve 5222 // Step 2. If IsPromise(x) is true, then 5223 if (isPromise) { 5224 // Step 2.a. Let xConstructor be ? Get(x, "constructor"). 5225 RootedValue ctorVal(cx); 5226 if (!GetProperty(cx, xObj, xObj, cx->names().constructor, &ctorVal)) { 5227 return nullptr; 5228 } 5229 5230 // Step 2.b. If SameValue(xConstructor, C) is true, return x. 5231 if (ctorVal == thisVal) { 5232 return xObj; 5233 } 5234 } 5235 } 5236 5237 // Promise.reject 5238 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 5239 // PromiseResolve 5240 // Step 3. Let promiseCapability be ? NewPromiseCapability(C). 5241 Rooted<PromiseCapability> capability(cx); 5242 if (!NewPromiseCapability(cx, C, &capability, true)) { 5243 return nullptr; 5244 } 5245 5246 HandleObject promise = capability.promise(); 5247 if (mode == ResolveMode) { 5248 // PromiseResolve 5249 // Step 4. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). 5250 if (!CallPromiseResolveFunction(cx, capability.resolve(), argVal, 5251 promise)) { 5252 return nullptr; 5253 } 5254 } else { 5255 // Promise.reject 5256 // Step 3. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). 5257 if (!CallPromiseRejectFunction(cx, capability.reject(), argVal, promise, 5258 nullptr, 5259 UnhandledRejectionBehavior::Report)) { 5260 return nullptr; 5261 } 5262 } 5263 5264 // Promise.reject 5265 // Step 4. Return promiseCapability.[[Promise]]. 5266 // PromiseResolve 5267 // Step 5. Return promiseCapability.[[Promise]]. 5268 return promise; 5269 } 5270 5271 [[nodiscard]] JSObject* js::PromiseResolve(JSContext* cx, 5272 HandleObject constructor, 5273 HandleValue value) { 5274 RootedValue C(cx, ObjectValue(*constructor)); 5275 return CommonStaticResolveRejectImpl(cx, C, value, ResolveMode); 5276 } 5277 5278 /** 5279 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5280 * 5281 * Promise.reject ( r ) 5282 * https://tc39.es/ecma262/#sec-promise.reject 5283 */ 5284 static bool Promise_reject(JSContext* cx, unsigned argc, Value* vp) { 5285 CallArgs args = CallArgsFromVp(argc, vp); 5286 HandleValue thisVal = args.thisv(); 5287 HandleValue argVal = args.get(0); 5288 JSObject* result = 5289 CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode); 5290 if (!result) { 5291 return false; 5292 } 5293 args.rval().setObject(*result); 5294 return true; 5295 } 5296 5297 /** 5298 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5299 * 5300 * Promise.reject ( r ) 5301 * https://tc39.es/ecma262/#sec-promise.reject 5302 * 5303 * Unforgeable version. 5304 */ 5305 /* static */ 5306 PromiseObject* PromiseObject::unforgeableReject(JSContext* cx, 5307 HandleValue value) { 5308 cx->check(value); 5309 5310 // Step 1. Let C be the this value. 5311 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 5312 Rooted<PromiseObject*> promise( 5313 cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); 5314 if (!promise) { 5315 return nullptr; 5316 } 5317 5318 MOZ_ASSERT(promise->state() == JS::PromiseState::Pending); 5319 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 5320 5321 // Step 3. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). 5322 if (!RejectPromiseInternal(cx, promise, value)) { 5323 return nullptr; 5324 } 5325 5326 // Step 4. Return promiseCapability.[[Promise]]. 5327 return promise; 5328 } 5329 5330 /** 5331 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5332 * 5333 * Promise.resolve ( x ) 5334 * https://tc39.es/ecma262/#sec-promise.resolve 5335 */ 5336 bool js::Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp) { 5337 CallArgs args = CallArgsFromVp(argc, vp); 5338 HandleValue thisVal = args.thisv(); 5339 HandleValue argVal = args.get(0); 5340 JSObject* result = 5341 CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode); 5342 if (!result) { 5343 return false; 5344 } 5345 args.rval().setObject(*result); 5346 return true; 5347 } 5348 5349 /** 5350 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5351 * 5352 * Promise.resolve ( x ) 5353 * https://tc39.es/ecma262/#sec-promise.resolve 5354 * 5355 * Unforgeable version. 5356 */ 5357 /* static */ 5358 JSObject* PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value) { 5359 JSObject* promiseCtor = JS::GetPromiseConstructor(cx); 5360 if (!promiseCtor) { 5361 return nullptr; 5362 } 5363 RootedValue cVal(cx, ObjectValue(*promiseCtor)); 5364 return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode); 5365 } 5366 5367 /** 5368 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5369 * 5370 * Promise.resolve ( x ) 5371 * https://tc39.es/ecma262/#sec-promise.resolve 5372 * PromiseResolve ( C, x ) 5373 * https://tc39.es/ecma262/#sec-promise-resolve 5374 * 5375 * Unforgeable version, where `x` is guaranteed not to be a promise. 5376 */ 5377 /* static */ 5378 PromiseObject* PromiseObject::unforgeableResolveWithNonPromise( 5379 JSContext* cx, HandleValue value) { 5380 cx->check(value); 5381 5382 #ifdef DEBUG 5383 auto IsPromise = [](HandleValue value) { 5384 if (!value.isObject()) { 5385 return false; 5386 } 5387 5388 JSObject* obj = &value.toObject(); 5389 if (obj->is<PromiseObject>()) { 5390 return true; 5391 } 5392 5393 if (!IsWrapper(obj)) { 5394 return false; 5395 } 5396 5397 return obj->canUnwrapAs<PromiseObject>(); 5398 }; 5399 MOZ_ASSERT(!IsPromise(value), "must use unforgeableResolve with this value"); 5400 #endif 5401 5402 // Promise.resolve 5403 // Step 3. Return ? PromiseResolve(C, x). 5404 5405 // PromiseResolve 5406 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 5407 Rooted<PromiseObject*> promise( 5408 cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); 5409 if (!promise) { 5410 return nullptr; 5411 } 5412 5413 MOZ_ASSERT(promise->state() == JS::PromiseState::Pending); 5414 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 5415 5416 // PromiseResolve 5417 // Step 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). 5418 if (!ResolvePromiseInternal(cx, promise, value)) { 5419 return nullptr; 5420 } 5421 5422 // PromiseResolve 5423 // Step 4. Return promiseCapability.[[Promise]]. 5424 return promise; 5425 } 5426 5427 /** 5428 * https://tc39.es/proposal-promise-try/ 5429 * 5430 * Promise.try ( ) 5431 */ 5432 static bool Promise_static_try(JSContext* cx, unsigned argc, Value* vp) { 5433 CallArgs args = CallArgsFromVp(argc, vp); 5434 5435 // 1. Let C be the this value. 5436 HandleValue cVal = args.thisv(); 5437 5438 // 2. If C is not an Object, throw a TypeError exception. 5439 if (!cVal.isObject()) { 5440 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5441 JSMSG_OBJECT_REQUIRED, 5442 "Receiver of Promise.try call"); 5443 return false; 5444 } 5445 5446 // 3. Let promiseCapability be ? NewPromiseCapability(C). 5447 RootedObject c(cx, &cVal.toObject()); 5448 Rooted<PromiseCapability> promiseCapability(cx); 5449 if (!NewPromiseCapability(cx, c, &promiseCapability, false)) { 5450 return false; 5451 } 5452 HandleObject promiseObject = promiseCapability.promise(); 5453 5454 // 4. Let status be Completion(Call(callbackfn, undefined, args)). 5455 size_t argCount = args.length(); 5456 if (argCount > 0) { 5457 argCount--; 5458 } 5459 5460 InvokeArgs iargs(cx); 5461 if (!iargs.init(cx, argCount)) { 5462 return false; 5463 } 5464 5465 for (size_t i = 0; i < argCount; i++) { 5466 iargs[i].set(args[i + 1]); 5467 } 5468 5469 HandleValue callbackfn = args.get(0); 5470 RootedValue rval(cx); 5471 bool ok = Call(cx, callbackfn, UndefinedHandleValue, iargs, &rval); 5472 5473 // 5. If status is an abrupt completion, then 5474 if (!ok) { 5475 RootedValue reason(cx); 5476 Rooted<SavedFrame*> stack(cx); 5477 5478 if (!MaybeGetAndClearExceptionAndStack(cx, &reason, &stack)) { 5479 return false; 5480 } 5481 5482 // 5.a. Perform ? Call(promiseCapability.[[Reject]], undefined, « 5483 // status.[[Value]] »). 5484 if (!CallPromiseRejectFunction(cx, promiseCapability.reject(), reason, 5485 promiseObject, stack, 5486 UnhandledRejectionBehavior::Report)) { 5487 return false; 5488 } 5489 } else { 5490 // 6. Else, 5491 // 6.a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « 5492 // status.[[Value]] »). 5493 if (!CallPromiseResolveFunction(cx, promiseCapability.resolve(), rval, 5494 promiseObject)) { 5495 return false; 5496 } 5497 } 5498 5499 // 7. Return promiseCapability.[[Promise]]. 5500 args.rval().setObject(*promiseObject); 5501 return true; 5502 } 5503 5504 /** 5505 * https://tc39.es/proposal-promise-with-resolvers/ 5506 * 5507 * Promise.withResolvers ( ) 5508 */ 5509 static bool Promise_static_withResolvers(JSContext* cx, unsigned argc, 5510 Value* vp) { 5511 CallArgs args = CallArgsFromVp(argc, vp); 5512 5513 // Step 1. Let C be the this value. 5514 HandleValue cVal = args.thisv(); 5515 5516 // Step 2. Let promiseCapability be ? NewPromiseCapability(C). 5517 if (!cVal.isObject()) { 5518 ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_SEARCH_STACK, cVal, 5519 nullptr); 5520 return false; 5521 } 5522 RootedObject c(cx, &cVal.toObject()); 5523 Rooted<PromiseCapability> promiseCapability(cx); 5524 if (!NewPromiseCapability(cx, c, &promiseCapability, false)) { 5525 return false; 5526 } 5527 5528 // Step 3. Let obj be OrdinaryObjectCreate(%Object.prototype%). 5529 Rooted<PlainObject*> obj(cx, NewPlainObject(cx)); 5530 if (!obj) { 5531 return false; 5532 } 5533 5534 // Step 4. Perform ! CreateDataPropertyOrThrow(obj, "promise", 5535 // promiseCapability.[[Promise]]). 5536 RootedValue v(cx, ObjectValue(*promiseCapability.promise())); 5537 if (!NativeDefineDataProperty(cx, obj, cx->names().promise, v, 5538 JSPROP_ENUMERATE)) { 5539 return false; 5540 } 5541 5542 // Step 5. Perform ! CreateDataPropertyOrThrow(obj, "resolve", 5543 // promiseCapability.[[Resolve]]). 5544 v.setObject(*promiseCapability.resolve()); 5545 if (!NativeDefineDataProperty(cx, obj, cx->names().resolve, v, 5546 JSPROP_ENUMERATE)) { 5547 return false; 5548 } 5549 5550 // Step 6. Perform ! CreateDataPropertyOrThrow(obj, "reject", 5551 // promiseCapability.[[Reject]]). 5552 v.setObject(*promiseCapability.reject()); 5553 if (!NativeDefineDataProperty(cx, obj, cx->names().reject, v, 5554 JSPROP_ENUMERATE)) { 5555 return false; 5556 } 5557 5558 // Step 7. Return obj. 5559 args.rval().setObject(*obj); 5560 return true; 5561 } 5562 5563 /** 5564 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5565 * 5566 * get Promise [ @@species ] 5567 * https://tc39.es/ecma262/#sec-get-promise-@@species 5568 */ 5569 bool js::Promise_static_species(JSContext* cx, unsigned argc, Value* vp) { 5570 CallArgs args = CallArgsFromVp(argc, vp); 5571 5572 // Step 1. Return the this value. 5573 args.rval().set(args.thisv()); 5574 return true; 5575 } 5576 5577 enum class HostDefinedDataObjectOption { 5578 // Allocate the host defined data object, this is the normal operation. 5579 Allocate, 5580 5581 // Do not allocate the host defined data object because the embeddings can 5582 // retrieve the same data on its own. 5583 OptimizeOut, 5584 5585 // Did not allocate the host defined data object because this is a special 5586 // case used by the debugger. 5587 UnusedForDebugger, 5588 }; 5589 5590 /** 5591 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5592 * 5593 * PerformPromiseThen ( promise, onFulfilled, onRejected 5594 * [ , resultCapability ] ) 5595 * https://tc39.es/ecma262/#sec-performpromisethen 5596 * 5597 * Steps 7-8 for creating PromiseReaction record. 5598 * We use single object for both fulfillReaction and rejectReaction. 5599 */ 5600 static PromiseReactionRecord* NewReactionRecord( 5601 JSContext* cx, Handle<PromiseCapability> resultCapability, 5602 HandleValue onFulfilled, HandleValue onRejected, 5603 HostDefinedDataObjectOption hostDefinedDataObjectOption) { 5604 #ifdef DEBUG 5605 if (resultCapability.promise()) { 5606 if (hostDefinedDataObjectOption == HostDefinedDataObjectOption::Allocate) { 5607 if (resultCapability.promise()->is<PromiseObject>()) { 5608 // If `resultCapability.promise` is a Promise object, 5609 // `resultCapability.{resolve,reject}` may be optimized out, 5610 // but if they're not, they should be callable. 5611 MOZ_ASSERT_IF(resultCapability.resolve(), 5612 IsCallable(resultCapability.resolve())); 5613 MOZ_ASSERT_IF(resultCapability.reject(), 5614 IsCallable(resultCapability.reject())); 5615 } else { 5616 // If `resultCapability.promise` is a non-Promise object 5617 // (including wrapped Promise object), 5618 // `resultCapability.{resolve,reject}` should be callable. 5619 MOZ_ASSERT(resultCapability.resolve()); 5620 MOZ_ASSERT(IsCallable(resultCapability.resolve())); 5621 MOZ_ASSERT(resultCapability.reject()); 5622 MOZ_ASSERT(IsCallable(resultCapability.reject())); 5623 } 5624 } else if (hostDefinedDataObjectOption == 5625 HostDefinedDataObjectOption::UnusedForDebugger) { 5626 // For debugger usage, `resultCapability.promise` should be a 5627 // maybe-wrapped Promise object. The other fields are not used. 5628 // 5629 // This is the only case where we allow `resolve` and `reject` to 5630 // be null when the `promise` field is not a PromiseObject. 5631 JSObject* unwrappedPromise = UncheckedUnwrap(resultCapability.promise()); 5632 MOZ_ASSERT(unwrappedPromise->is<PromiseObject>() || 5633 JS_IsDeadWrapper(unwrappedPromise)); 5634 MOZ_ASSERT(!resultCapability.resolve()); 5635 MOZ_ASSERT(!resultCapability.reject()); 5636 } 5637 } else { 5638 // `resultCapability.promise` is null for the following cases: 5639 // * resulting Promise is known to be unused 5640 // * Async Function 5641 // * Async Generator 5642 // In any case, other fields are also not used. 5643 MOZ_ASSERT(!resultCapability.resolve()); 5644 MOZ_ASSERT(!resultCapability.reject()); 5645 MOZ_ASSERT(hostDefinedDataObjectOption != 5646 HostDefinedDataObjectOption::UnusedForDebugger); 5647 } 5648 #endif 5649 5650 // Ensure the onFulfilled handler has the expected type. 5651 MOZ_ASSERT(onFulfilled.isInt32() || onFulfilled.isObjectOrNull()); 5652 MOZ_ASSERT_IF(onFulfilled.isObject(), IsCallable(onFulfilled)); 5653 MOZ_ASSERT_IF(onFulfilled.isInt32(), 5654 0 <= onFulfilled.toInt32() && 5655 onFulfilled.toInt32() < int32_t(PromiseHandler::Limit)); 5656 5657 // Ensure the onRejected handler has the expected type. 5658 MOZ_ASSERT(onRejected.isInt32() || onRejected.isObjectOrNull()); 5659 MOZ_ASSERT_IF(onRejected.isObject(), IsCallable(onRejected)); 5660 MOZ_ASSERT_IF(onRejected.isInt32(), 5661 0 <= onRejected.toInt32() && 5662 onRejected.toInt32() < int32_t(PromiseHandler::Limit)); 5663 5664 // Handlers must either both be present or both be absent. 5665 MOZ_ASSERT(onFulfilled.isNull() == onRejected.isNull()); 5666 5667 RootedObject hostDefinedData(cx); 5668 if (hostDefinedDataObjectOption == HostDefinedDataObjectOption::Allocate) { 5669 if (!GetObjectFromHostDefinedData(cx, &hostDefinedData)) { 5670 return nullptr; 5671 } 5672 } 5673 5674 PromiseReactionRecord* reaction = 5675 NewBuiltinClassInstance<PromiseReactionRecord>(cx); 5676 if (!reaction) { 5677 return nullptr; 5678 } 5679 cx->check(resultCapability.promise(), onFulfilled, onRejected, 5680 resultCapability.resolve(), resultCapability.reject(), 5681 hostDefinedData); 5682 5683 // Step 7. Let fulfillReaction be the PromiseReaction 5684 // { [[Capability]]: resultCapability, [[Type]]: Fulfill, 5685 // [[Handler]]: onFulfilledJobCallback }. 5686 // Step 8. Let rejectReaction be the PromiseReaction 5687 // { [[Capability]]: resultCapability, [[Type]]: Reject, 5688 // [[Handler]]: onRejectedJobCallback }. 5689 5690 // See comments for ReactionRecordSlots for the relation between 5691 // spec record fields and PromiseReactionRecord slots. 5692 reaction->initFixedSlot(PromiseReactionRecord::Promise, 5693 ObjectOrNullValue(resultCapability.promise())); 5694 // We set [[Type]] in EnqueuePromiseReactionJob, by calling 5695 // setTargetStateAndHandlerArg. 5696 reaction->initFixedSlot(PromiseReactionRecord::Flags, Int32Value(0)); 5697 reaction->initFixedSlot(PromiseReactionRecord::OnFulfilled, onFulfilled); 5698 reaction->initFixedSlot(PromiseReactionRecord::OnRejected, onRejected); 5699 reaction->initFixedSlot(PromiseReactionRecord::Resolve, 5700 ObjectOrNullValue(resultCapability.resolve())); 5701 reaction->initFixedSlot(PromiseReactionRecord::Reject, 5702 ObjectOrNullValue(resultCapability.reject())); 5703 reaction->initFixedSlot(PromiseReactionRecord::HostDefinedData, 5704 ObjectOrNullValue(hostDefinedData)); 5705 5706 return reaction; 5707 } 5708 5709 static bool IsPromiseSpecies(JSContext* cx, JSFunction* species) { 5710 return species->maybeNative() == Promise_static_species; 5711 } 5712 5713 // Whether to create a promise as the return value of Promise#{then,catch}. 5714 // If the return value is known to be unused, and if the operation is known 5715 // to be unobservable, we can skip creating the promise. 5716 enum class CreateDependentPromise { Always, SkipIfCtorUnobservable }; 5717 5718 /** 5719 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5720 * 5721 * Promise.prototype.then ( onFulfilled, onRejected ) 5722 * https://tc39.es/ecma262/#sec-promise.prototype.then 5723 * 5724 * Steps 3-4. 5725 */ 5726 static bool PromiseThenNewPromiseCapability( 5727 JSContext* cx, HandleObject promiseObj, 5728 CreateDependentPromise createDependent, 5729 MutableHandle<PromiseCapability> resultCapability) { 5730 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 5731 RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, 5732 IsPromiseSpecies)); 5733 if (!C) { 5734 return false; 5735 } 5736 5737 if (createDependent != CreateDependentPromise::Always && 5738 IsNativeFunction(C, PromiseConstructor)) { 5739 return true; 5740 } 5741 5742 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 5743 if (!NewPromiseCapability(cx, C, resultCapability, true)) { 5744 return false; 5745 } 5746 5747 JSObject* unwrappedPromise = promiseObj; 5748 if (IsWrapper(promiseObj)) { 5749 unwrappedPromise = UncheckedUnwrap(promiseObj); 5750 } 5751 JSObject* unwrappedNewPromise = resultCapability.promise(); 5752 if (IsWrapper(resultCapability.promise())) { 5753 unwrappedNewPromise = UncheckedUnwrap(resultCapability.promise()); 5754 } 5755 if (unwrappedPromise->is<PromiseObject>() && 5756 unwrappedNewPromise->is<PromiseObject>()) { 5757 unwrappedNewPromise->as<PromiseObject>().copyUserInteractionFlagsFrom( 5758 unwrappedPromise->as<PromiseObject>()); 5759 } 5760 5761 return true; 5762 } 5763 5764 /** 5765 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5766 * 5767 * Promise.prototype.then ( onFulfilled, onRejected ) 5768 * https://tc39.es/ecma262/#sec-promise.prototype.then 5769 * 5770 * Steps 3-5. 5771 */ 5772 [[nodiscard]] PromiseObject* js::OriginalPromiseThen(JSContext* cx, 5773 HandleObject promiseObj, 5774 HandleObject onFulfilled, 5775 HandleObject onRejected) { 5776 cx->check(promiseObj, onFulfilled, onRejected); 5777 5778 RootedValue promiseVal(cx, ObjectValue(*promiseObj)); 5779 Rooted<PromiseObject*> unwrappedPromise( 5780 cx, 5781 UnwrapAndTypeCheckValue<PromiseObject>(cx, promiseVal, [cx, promiseObj] { 5782 JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, 5783 JSMSG_INCOMPATIBLE_PROTO, "Promise", "then", 5784 promiseObj->getClass()->name); 5785 })); 5786 if (!unwrappedPromise) { 5787 return nullptr; 5788 } 5789 5790 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 5791 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 5792 Rooted<PromiseObject*> newPromise( 5793 cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); 5794 if (!newPromise) { 5795 return nullptr; 5796 } 5797 newPromise->copyUserInteractionFlagsFrom(*unwrappedPromise); 5798 5799 Rooted<PromiseCapability> resultCapability(cx); 5800 resultCapability.promise().set(newPromise); 5801 5802 // Step 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 5803 // resultCapability). 5804 { 5805 RootedValue onFulfilledVal(cx, ObjectOrNullValue(onFulfilled)); 5806 RootedValue onRejectedVal(cx, ObjectOrNullValue(onRejected)); 5807 if (!PerformPromiseThen(cx, unwrappedPromise, onFulfilledVal, onRejectedVal, 5808 resultCapability)) { 5809 return nullptr; 5810 } 5811 } 5812 5813 return newPromise; 5814 } 5815 5816 /** 5817 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5818 * 5819 * Promise.prototype.then ( onFulfilled, onRejected ) 5820 * https://tc39.es/ecma262/#sec-promise.prototype.then 5821 * 5822 * Steps 3-5. 5823 */ 5824 [[nodiscard]] static bool OriginalPromiseThenWithoutSettleHandlers( 5825 JSContext* cx, Handle<PromiseObject*> promise, 5826 Handle<PromiseObject*> promiseToResolve) { 5827 cx->check(promise); 5828 5829 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 5830 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 5831 Rooted<PromiseCapability> resultCapability(cx); 5832 if (!PromiseThenNewPromiseCapability( 5833 cx, promise, CreateDependentPromise::SkipIfCtorUnobservable, 5834 &resultCapability)) { 5835 return false; 5836 } 5837 5838 // Step 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 5839 // resultCapability). 5840 return PerformPromiseThenWithoutSettleHandlers(cx, promise, promiseToResolve, 5841 resultCapability); 5842 } 5843 5844 [[nodiscard]] static bool PerformPromiseThenWithReaction( 5845 JSContext* cx, Handle<PromiseObject*> promise, 5846 Handle<PromiseReactionRecord*> reaction); 5847 5848 [[nodiscard]] bool js::ReactToUnwrappedPromise( 5849 JSContext* cx, Handle<PromiseObject*> unwrappedPromise, 5850 HandleObject onFulfilled_, HandleObject onRejected_, 5851 UnhandledRejectionBehavior behavior) { 5852 cx->check(onFulfilled_, onRejected_); 5853 5854 MOZ_ASSERT_IF(onFulfilled_, IsCallable(onFulfilled_)); 5855 MOZ_ASSERT_IF(onRejected_, IsCallable(onRejected_)); 5856 5857 RootedValue onFulfilled( 5858 cx, onFulfilled_ ? ObjectValue(*onFulfilled_) 5859 : Int32Value(int32_t(PromiseHandler::Identity))); 5860 5861 RootedValue onRejected( 5862 cx, onRejected_ ? ObjectValue(*onRejected_) 5863 : Int32Value(int32_t(PromiseHandler::Thrower))); 5864 5865 Rooted<PromiseCapability> resultCapability(cx); 5866 MOZ_ASSERT(!resultCapability.promise()); 5867 5868 auto hostDefinedDataObjectOption = 5869 unwrappedPromise->state() == JS::PromiseState::Pending 5870 ? HostDefinedDataObjectOption::Allocate 5871 : HostDefinedDataObjectOption::OptimizeOut; 5872 5873 Rooted<PromiseReactionRecord*> reaction( 5874 cx, NewReactionRecord(cx, resultCapability, onFulfilled, onRejected, 5875 hostDefinedDataObjectOption)); 5876 if (!reaction) { 5877 return false; 5878 } 5879 5880 if (behavior == UnhandledRejectionBehavior::Ignore) { 5881 reaction->setShouldIgnoreUnhandledRejection(); 5882 } 5883 5884 return PerformPromiseThenWithReaction(cx, unwrappedPromise, reaction); 5885 } 5886 5887 static bool CanCallOriginalPromiseThenBuiltin(JSContext* cx, 5888 HandleValue promise) { 5889 return promise.isObject() && promise.toObject().is<PromiseObject>() && 5890 IsPromiseWithDefaultProperties(&promise.toObject().as<PromiseObject>(), 5891 cx); 5892 } 5893 5894 static MOZ_ALWAYS_INLINE bool IsPromiseThenOrCatchRetValImplicitlyUsed( 5895 JSContext* cx, PromiseObject* promise) { 5896 // Embedding requires the return value of then/catch as 5897 // `enqueuePromiseJob` parameter, to propaggate the user-interaction. 5898 // We cannot optimize out the return value if the flag is set by embedding. 5899 if (promise->requiresUserInteractionHandling()) { 5900 return true; 5901 } 5902 5903 // The returned promise of Promise#then and Promise#catch contains 5904 // stack info if async stack is enabled. Even if their return value is not 5905 // used explicitly in the script, the stack info is observable in devtools 5906 // and profilers. We shouldn't apply the optimization not to allocate the 5907 // returned Promise object if the it's implicitly used by them. 5908 if (!cx->options().asyncStack()) { 5909 return false; 5910 } 5911 5912 // If devtools is opened, the current realm will become debuggee. 5913 if (cx->realm()->isDebuggee()) { 5914 return true; 5915 } 5916 5917 // There are 2 profilers, and they can be independently enabled. 5918 if (cx->runtime()->geckoProfiler().enabled()) { 5919 return true; 5920 } 5921 if (JS::IsProfileTimelineRecordingEnabled()) { 5922 return true; 5923 } 5924 5925 // The stack is also observable from Error#stack, but we don't care since 5926 // it's nonstandard feature. 5927 return false; 5928 } 5929 5930 /** 5931 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 5932 * 5933 * Promise.prototype.then ( onFulfilled, onRejected ) 5934 * https://tc39.es/ecma262/#sec-promise.prototype.then 5935 * 5936 * Steps 3-5. 5937 */ 5938 static bool OriginalPromiseThenBuiltin(JSContext* cx, HandleValue promiseVal, 5939 HandleValue onFulfilled, 5940 HandleValue onRejected, 5941 MutableHandleValue rval, 5942 bool rvalExplicitlyUsed) { 5943 cx->check(promiseVal, onFulfilled, onRejected); 5944 MOZ_ASSERT(CanCallOriginalPromiseThenBuiltin(cx, promiseVal)); 5945 5946 Rooted<PromiseObject*> promise(cx, 5947 &promiseVal.toObject().as<PromiseObject>()); 5948 5949 bool rvalUsed = rvalExplicitlyUsed || 5950 IsPromiseThenOrCatchRetValImplicitlyUsed(cx, promise); 5951 5952 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 5953 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 5954 Rooted<PromiseCapability> resultCapability(cx); 5955 if (rvalUsed) { 5956 PromiseObject* resultPromise = 5957 CreatePromiseObjectWithoutResolutionFunctions(cx); 5958 if (!resultPromise) { 5959 return false; 5960 } 5961 5962 resultPromise->copyUserInteractionFlagsFrom( 5963 promiseVal.toObject().as<PromiseObject>()); 5964 resultCapability.promise().set(resultPromise); 5965 } 5966 5967 // Step 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 5968 // resultCapability). 5969 if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, 5970 resultCapability)) { 5971 return false; 5972 } 5973 5974 if (rvalUsed) { 5975 rval.setObject(*resultCapability.promise()); 5976 } else { 5977 rval.setUndefined(); 5978 } 5979 return true; 5980 } 5981 5982 [[nodiscard]] bool js::RejectPromiseWithPendingError( 5983 JSContext* cx, Handle<PromiseObject*> promise) { 5984 cx->check(promise); 5985 5986 if (!cx->isExceptionPending()) { 5987 // Reject the promise, but also propagate this uncatchable error. 5988 (void)PromiseObject::reject(cx, promise, UndefinedHandleValue); 5989 return false; 5990 } 5991 5992 RootedValue exn(cx); 5993 if (!GetAndClearException(cx, &exn)) { 5994 return false; 5995 } 5996 return PromiseObject::reject(cx, promise, exn); 5997 } 5998 5999 // Some async/await functions are implemented here instead of 6000 // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions. 6001 6002 /** 6003 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6004 * 6005 * Runtime Semantics: EvaluateAsyncFunctionBody 6006 * AsyncFunctionBody : FunctionBody 6007 * https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncfunctionbody 6008 * 6009 * Runtime Semantics: EvaluateAsyncConciseBody 6010 * AsyncConciseBody : ExpressionBody 6011 * https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncconcisebody 6012 */ 6013 [[nodiscard]] PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx) { 6014 // Step 1. Let promiseCapability be ! NewPromiseCapability(%Promise%). 6015 PromiseObject* promise = CreatePromiseObjectWithoutResolutionFunctions(cx); 6016 if (!promise) { 6017 return nullptr; 6018 } 6019 6020 AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC); 6021 return promise; 6022 } 6023 6024 bool js::IsPromiseForAsyncFunctionOrGenerator(JSObject* promise) { 6025 return promise->is<PromiseObject>() && 6026 PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC); 6027 } 6028 6029 [[nodiscard]] PromiseObject* js::CreatePromiseObjectForAsyncGenerator( 6030 JSContext* cx) { 6031 PromiseObject* promise = CreatePromiseObjectWithoutResolutionFunctions(cx); 6032 if (!promise) { 6033 return nullptr; 6034 } 6035 6036 AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC); 6037 return promise; 6038 } 6039 6040 /** 6041 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6042 * 6043 * AsyncFunctionStart ( promiseCapability, asyncFunctionBody ) 6044 * https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start 6045 * 6046 * Steps 4.f-g. 6047 */ 6048 [[nodiscard]] bool js::AsyncFunctionThrown( 6049 JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue reason, 6050 JS::Handle<SavedFrame*> unwrappedRejectionStack) { 6051 if (resultPromise->state() != JS::PromiseState::Pending) { 6052 // OOM after resolving promise. 6053 // Report a warning and ignore the result. 6054 if (!WarnNumberASCII(cx, JSMSG_UNHANDLABLE_PROMISE_REJECTION_WARNING)) { 6055 if (cx->isExceptionPending()) { 6056 cx->clearPendingException(); 6057 } 6058 } 6059 return true; 6060 } 6061 6062 // Step 4.f. Else, 6063 // Step 4.f.i. Assert: result.[[Type]] is throw. 6064 // Step 4.f.ii. Perform 6065 // ! Call(promiseCapability.[[Reject]], undefined, 6066 // « result.[[Value]] »). 6067 // Step 4.g. Return. 6068 return RejectPromiseInternal(cx, resultPromise, reason, 6069 unwrappedRejectionStack); 6070 } 6071 6072 /** 6073 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6074 * 6075 * AsyncFunctionStart ( promiseCapability, asyncFunctionBody ) 6076 * https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start 6077 * 6078 * Steps 4.e, 4.g. 6079 */ 6080 [[nodiscard]] bool js::AsyncFunctionReturned( 6081 JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) { 6082 // Step 4.e. Else if result.[[Type]] is return, then 6083 // Step 4.e.i. Perform 6084 // ! Call(promiseCapability.[[Resolve]], undefined, 6085 // « result.[[Value]] »). 6086 return ResolvePromiseInternal(cx, resultPromise, value); 6087 } 6088 6089 /** 6090 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6091 * 6092 * Await 6093 * https://tc39.github.io/ecma262/#await 6094 * 6095 * Helper function that performs Await(promise) steps 2-7. 6096 * The same steps are also used in a few other places in the spec. 6097 */ 6098 template <typename T> 6099 [[nodiscard]] static bool InternalAwait(JSContext* cx, HandleValue value, 6100 HandleObject resultPromise, 6101 PromiseHandler onFulfilled, 6102 PromiseHandler onRejected, 6103 T extraStep) { 6104 // Step 2. Let promise be ? PromiseResolve(%Promise%, value). 6105 RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, value)); 6106 if (!promise) { 6107 return false; 6108 } 6109 6110 // This downcast is safe because unforgeableResolve either returns `value` 6111 // (only if it is already a possibly-wrapped promise) or creates a new 6112 // promise using the Promise constructor. 6113 Rooted<PromiseObject*> unwrappedPromise( 6114 cx, UnwrapAndDowncastObject<PromiseObject>(cx, promise)); 6115 if (!unwrappedPromise) { 6116 return false; 6117 } 6118 6119 // Steps 3-6 for creating onFulfilled/onRejected are done by caller. 6120 6121 // Step 7. Perform ! PerformPromiseThen(promise, onFulfilled, onRejected). 6122 RootedValue onFulfilledValue(cx, Int32Value(int32_t(onFulfilled))); 6123 RootedValue onRejectedValue(cx, Int32Value(int32_t(onRejected))); 6124 Rooted<PromiseCapability> resultCapability(cx); 6125 resultCapability.promise().set(resultPromise); 6126 6127 auto hostDefinedDataObjectOption = 6128 unwrappedPromise->state() == JS::PromiseState::Pending 6129 ? HostDefinedDataObjectOption::Allocate 6130 : HostDefinedDataObjectOption::OptimizeOut; 6131 6132 Rooted<PromiseReactionRecord*> reaction( 6133 cx, NewReactionRecord(cx, resultCapability, onFulfilledValue, 6134 onRejectedValue, hostDefinedDataObjectOption)); 6135 if (!reaction) { 6136 return false; 6137 } 6138 extraStep(reaction); 6139 return PerformPromiseThenWithReaction(cx, unwrappedPromise, reaction); 6140 } 6141 6142 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 6143 // Explicit Resource Management Proposal 6144 // 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncDispose ] ( ) 6145 // Steps 6.c-g 6146 // https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-%25asynciteratorprototype%25-%40%40asyncdispose 6147 // The steps mentioned above are almost identical to the steps 3-7 of 6148 // https://tc39.es/ecma262/#await we have a utility function InternalAwait which 6149 // covers these steps thus this following function wraps around the utility 6150 // and implements the steps of %AsyncIteratorPrototype% [ @@asyncDispose ] ( ). 6151 [[nodiscard]] bool js::InternalAsyncIteratorDisposeAwait( 6152 JSContext* cx, JS::Handle<JS::Value> value, 6153 JS::Handle<JSObject*> resultPromise) { 6154 auto extra = [](JS::Handle<PromiseReactionRecord*> reaction) {}; 6155 return InternalAwait(cx, value, resultPromise, 6156 PromiseHandler::AsyncIteratorDisposeAwaitFulfilled, 6157 PromiseHandler::Thrower, extra); 6158 } 6159 #endif 6160 6161 [[nodiscard]] bool js::InternalAsyncGeneratorAwait( 6162 JSContext* cx, JS::Handle<AsyncGeneratorObject*> generator, 6163 JS::Handle<JS::Value> value, PromiseHandler onFulfilled, 6164 PromiseHandler onRejected) { 6165 auto extra = [&](Handle<PromiseReactionRecord*> reaction) { 6166 reaction->setIsAsyncGenerator(generator); 6167 }; 6168 return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); 6169 } 6170 6171 /** 6172 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6173 * 6174 * Await 6175 * https://tc39.es/ecma262/#await 6176 */ 6177 [[nodiscard]] JSObject* js::AsyncFunctionAwait( 6178 JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj, 6179 HandleValue value) { 6180 auto extra = [&](Handle<PromiseReactionRecord*> reaction) { 6181 MOZ_ASSERT(genObj->realm() == reaction->realm()); 6182 MOZ_ASSERT(genObj->realm() == cx->realm()); 6183 reaction->setIsAsyncFunction(genObj); 6184 }; 6185 if (!InternalAwait(cx, value, nullptr, 6186 PromiseHandler::AsyncFunctionAwaitedFulfilled, 6187 PromiseHandler::AsyncFunctionAwaitedRejected, extra)) { 6188 return nullptr; 6189 } 6190 return genObj->promise(); 6191 } 6192 6193 /** 6194 * ES2026 draft rev d14670224281909f5bb552e8ebe4a8e958646c16 6195 * 6196 * %AsyncFromSyncIteratorPrototype%.next ( [ value ] ) 6197 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next 6198 * 6199 * %AsyncFromSyncIteratorPrototype%.return ( [ value ] ) 6200 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return 6201 * 6202 * %AsyncFromSyncIteratorPrototype%.throw ( [ value ] ) 6203 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw 6204 * 6205 * AsyncFromSyncIteratorContinuation ( result, promiseCapability, 6206 * syncIteratorRecord, closeOnRejection ) 6207 * https://tc39.es/ecma262/#sec-asyncfromsynciteratorcontinuation 6208 */ 6209 bool js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, 6210 CompletionKind completionKind) { 6211 // Step 1. Let O be the this value. 6212 HandleValue thisVal = args.thisv(); 6213 6214 // Step 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal 6215 // slot. 6216 MOZ_ASSERT(thisVal.isObject()); 6217 MOZ_ASSERT(thisVal.toObject().is<AsyncFromSyncIteratorObject>()); 6218 6219 // Step 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). 6220 Rooted<PromiseObject*> resultPromise( 6221 cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); 6222 if (!resultPromise) { 6223 return false; 6224 } 6225 6226 Rooted<AsyncFromSyncIteratorObject*> asyncIter( 6227 cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>()); 6228 6229 // next(): 6230 // Step 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. 6231 // 6232 // or 6233 // 6234 // return() / throw(): 6235 // Step 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]]. 6236 // Step 5. Let syncIterator be syncIteratorRecord.[[Iterator]]. 6237 RootedObject iter(cx, asyncIter->iterator()); 6238 6239 RootedValue func(cx); 6240 if (completionKind == CompletionKind::Normal) { 6241 // next() preparing for steps 5-6. 6242 func.set(asyncIter->nextMethod()); 6243 } else if (completionKind == CompletionKind::Return) { 6244 // return() steps 6-8. 6245 // Step 6. Let return be Completion(GetMethod(syncIterator, "return")). 6246 // Step 7. IfAbruptRejectPromise(return, promiseCapability). 6247 if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) { 6248 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6249 } 6250 6251 // Step 8. If return is undefined, then 6252 // (Note: GetMethod contains a step that changes `null` to `undefined`; 6253 // we omit that step above, and check for `null` here instead.) 6254 if (func.isNullOrUndefined()) { 6255 // Step 8.a. Let iterResult be CreateIterResultObject(value, true). 6256 PlainObject* resultObj = CreateIterResultObject(cx, args.get(0), true); 6257 if (!resultObj) { 6258 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6259 } 6260 6261 RootedValue resultVal(cx, ObjectValue(*resultObj)); 6262 6263 // Step 8.b. Perform ! Call(promiseCapability.[[Resolve]], undefined, 6264 // « iterResult »). 6265 if (!ResolvePromiseInternal(cx, resultPromise, resultVal)) { 6266 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6267 } 6268 6269 // Step 8.c. Return promiseCapability.[[Promise]]. 6270 args.rval().setObject(*resultPromise); 6271 return true; 6272 } 6273 } else { 6274 // throw() steps 6-8. 6275 MOZ_ASSERT(completionKind == CompletionKind::Throw); 6276 6277 // Step 6. Let throw be Completion(GetMethod(syncIterator, "throw")). 6278 // Step 7. IfAbruptRejectPromise(throw, promiseCapability). 6279 if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) { 6280 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6281 } 6282 6283 // Step 8. If throw is undefined, then 6284 // (Note: GetMethod contains a step that changes `null` to `undefined`; 6285 // we omit that step above, and check for `null` here instead.) 6286 if (func.isNullOrUndefined()) { 6287 // Step 8.a. NOTE: If syncIterator does not have a throw method, close it 6288 // to give it a chance to clean up before we reject the 6289 // capability. 6290 // Step 8.b. Let closeCompletion be NormalCompletion(empty). 6291 // Step 8.c. Let result be Completion(IteratorClose(syncIteratorRecord, 6292 // closeCompletion)). 6293 // Step 8.d. IfAbruptRejectPromise(result, promiseCapability). 6294 if (!CloseIterOperation(cx, iter, CompletionKind::Normal)) { 6295 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6296 } 6297 6298 // Step 8.e. NOTE: The next step throws a TypeError to indicate that there 6299 // was a protocol violation: syncIterator does not have a throw 6300 // method. 6301 // Step 8.f. NOTE: If closing syncIterator does not throw then the result 6302 // of that operation is ignored, even if it yields a rejected 6303 // promise. 6304 // Step 8.g. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, 6305 // « a newly created *TypeError* object »). 6306 Rooted<Value> noThrowMethodError(cx); 6307 if (!GetTypeError(cx, JSMSG_ITERATOR_NO_THROW, &noThrowMethodError)) { 6308 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6309 } 6310 if (!RejectPromiseInternal(cx, resultPromise, noThrowMethodError)) { 6311 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6312 } 6313 6314 // Step 8.h. Return promiseCapability.[[Promise]]. 6315 args.rval().setObject(*resultPromise); 6316 return true; 6317 } 6318 } 6319 6320 // next(): 6321 // Step 5. If value is present, then 6322 // Step 5.a. Let result be Completion(IteratorNext(syncIteratorRecord, 6323 // value)). 6324 // Step 6. Else, 6325 // Step 6.a. Let result be Completion(IteratorNext(syncIteratorRecord)). 6326 // 6327 // or 6328 // 6329 // return(): 6330 // Step 9. If value is present, then 6331 // Step 9.a. Let result be Completion(Call(return, syncIterator, 6332 // « value »)). 6333 // Step 10. Else, 6334 // Step 10.a. Let result be Completion(Call(return, syncIterator)). 6335 // 6336 // throw(): 6337 // Step 9. If value is present, then 6338 // Step 9.a. Let result be Completion(Call(throw, syncIterator, 6339 // « value »)). 6340 // Step 10. Else, 6341 // Step 10.a. Let result be Completion(Call(throw, syncIterator)). 6342 RootedValue iterVal(cx, ObjectValue(*iter)); 6343 RootedValue resultVal(cx); 6344 bool ok; 6345 if (args.length() == 0) { 6346 ok = Call(cx, func, iterVal, &resultVal); 6347 } else { 6348 ok = Call(cx, func, iterVal, args[0], &resultVal); 6349 } 6350 if (!ok) { 6351 // next(): 6352 // Step 7. IfAbruptRejectPromise(result, promiseCapability). 6353 // 6354 // return() / throw(): 6355 // Step 11. IfAbruptRejectPromise(result, promiseCapability). 6356 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6357 } 6358 6359 // next() steps 5-6 -> IteratorNext: 6360 // Step 5. If result is not an Object, throw a TypeError exception. 6361 // next(): 6362 // Step 7. IfAbruptRejectPromise(result, promiseCapability). 6363 // 6364 // or 6365 // 6366 // return() / throw(): 6367 // Step 12. If result is not an Object, then 6368 // Step 12.a. Perform ! Call(promiseCapability.[[Reject]], undefined, 6369 // « a newly created TypeError object »). 6370 // Step 12.b. Return promiseCapability.[[Promise]]. 6371 if (!resultVal.isObject()) { 6372 CheckIsObjectKind kind; 6373 switch (completionKind) { 6374 case CompletionKind::Normal: 6375 kind = CheckIsObjectKind::IteratorNext; 6376 break; 6377 case CompletionKind::Throw: 6378 kind = CheckIsObjectKind::IteratorThrow; 6379 break; 6380 case CompletionKind::Return: 6381 kind = CheckIsObjectKind::IteratorReturn; 6382 break; 6383 } 6384 MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind)); 6385 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6386 } 6387 6388 RootedObject resultObj(cx, &resultVal.toObject()); 6389 6390 // next(): 6391 // Step 8. Return AsyncFromSyncIteratorContinuation(result, 6392 // promiseCapability, 6393 // syncIteratorRecord, 6394 // true). 6395 // 6396 // return(): 6397 // Step 13. Return AsyncFromSyncIteratorContinuation(result, 6398 // promiseCapability, 6399 // syncIteratorRecord, 6400 // false). 6401 // 6402 // throw(): 6403 // Step 13. Return AsyncFromSyncIteratorContinuation(result, 6404 // promiseCapability, 6405 // syncIteratorRecord, 6406 // true). 6407 6408 // AsyncFromSyncIteratorContinuation is passed |closeOnRejection = true| iff 6409 // completion-kind is not "return". 6410 bool closeOnRejection = completionKind != CompletionKind::Return; 6411 6412 // The step numbers below are for AsyncFromSyncIteratorContinuation(). 6413 // 6414 // Step 1. NOTE: Because promiseCapability is derived from the intrinsic 6415 // %Promise%, the calls to promiseCapability.[[Reject]] entailed by 6416 // the use IfAbruptRejectPromise below are guaranteed not to throw. 6417 // Step 2. Let done be Completion(IteratorComplete(result)). 6418 // Step 3. IfAbruptRejectPromise(done, promiseCapability). 6419 RootedValue doneVal(cx); 6420 if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) { 6421 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6422 } 6423 bool done = ToBoolean(doneVal); 6424 6425 // Step 4. Let value be Completion(IteratorValue(result)). 6426 // Step 5. IfAbruptRejectPromise(value, promiseCapability). 6427 RootedValue value(cx); 6428 if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) { 6429 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6430 } 6431 6432 // Step 9. Let unwrap be a new Abstract Closure with parameters (v) that 6433 // captures done and performs the following steps when called: 6434 // Step 9.a. Return CreateIterResultObject(v, done). 6435 // Step 10. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, "", « »). 6436 // Step 11. NOTE: onFulfilled is used when processing the "value" property 6437 // of an IteratorResult object in order to wait for its value if it 6438 // is a promise and re-package the result in a new "unwrapped" 6439 // IteratorResult object. 6440 PromiseHandler onFulfilled = 6441 done ? PromiseHandler::AsyncFromSyncIteratorValueUnwrapDone 6442 : PromiseHandler::AsyncFromSyncIteratorValueUnwrapNotDone; 6443 6444 // Step 12. If done is true, or if closeOnRejection is false, then 6445 // Step 12.a. Let onRejected be undefined. 6446 // Step 13. Else, 6447 // Step 13.a. Let closeIterator be a new Abstract Closure with parameters 6448 // (error) that captures syncIteratorRecord and performs the 6449 // following steps when called: 6450 // Step 13.a.i. Return ? IteratorClose(syncIteratorRecord, 6451 // ThrowCompletion(error)). 6452 // Step 13.b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, "", 6453 // «»). 6454 // Step 13.c. NOTE: onRejected is used to close the Iterator when the "value" 6455 // property of an IteratorResult object it yields is a rejected 6456 // promise. 6457 PromiseHandler onRejected = done || !closeOnRejection 6458 ? PromiseHandler::Thrower 6459 : PromiseHandler::AsyncFromSyncIteratorClose; 6460 6461 // Steps 6, 8, and 14 are identical to some steps in Await; we have a utility 6462 // function InternalAwait() that implements the idiom. 6463 // 6464 // Step 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)). 6465 // Step 8. IfAbruptRejectPromise(valueWrapper, promiseCapability). 6466 // Step 14. Perform PerformPromiseThen(valueWrapper, onFulfilled, 6467 // onRejected, promiseCapability). 6468 auto extra = [&](Handle<PromiseReactionRecord*> reaction) { 6469 if (onRejected == PromiseHandler::AsyncFromSyncIteratorClose) { 6470 reaction->setIsAsyncFromSyncIterator(asyncIter); 6471 } 6472 }; 6473 if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, 6474 extra)) { 6475 // Reordering step 7 to this point is fine, because there are no other 6476 // user-observable operations between steps 7-8 and 14. And |InternalAwait|, 6477 // apart from the initial PromiseResolve, can only fail due to OOM. Since 6478 // out-of-memory handling is not defined in the spec, we're also free 6479 // reorder its effects. So w.r.t. spec-observable steps, calling 6480 // |IteratorCloseForException| after |InternalAwait| has the same effect as 6481 // performing IteratorClose directly after PromiseResolve. 6482 // 6483 // Step 7. If valueWrapper is an abrupt completion, done is false, and 6484 // closeOnRejection is true, then 6485 // Step 7.a. Set valueWrapper to Completion(IteratorClose( 6486 // syncIteratorRecord, valueWrapper)). 6487 // 6488 // Check |cx->isExceptionPending()| because we don't want to perform 6489 // IteratorClose for uncatchable exceptions. (IteratorCloseForException will 6490 // also assert when there's no pending exception.) 6491 if (cx->isExceptionPending() && !done && closeOnRejection) { 6492 (void)IteratorCloseForException(cx, iter); 6493 } 6494 return AbruptRejectPromise(cx, args, resultPromise, nullptr); 6495 } 6496 6497 // Step 15. Return promiseCapability.[[Promise]]. 6498 args.rval().setObject(*resultPromise); 6499 return true; 6500 } 6501 6502 /** 6503 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6504 * 6505 * Promise.prototype.catch ( onRejected ) 6506 * https://tc39.es/ecma262/#sec-promise.prototype.catch 6507 */ 6508 static bool Promise_catch_impl(JSContext* cx, unsigned argc, Value* vp, 6509 bool rvalExplicitlyUsed) { 6510 CallArgs args = CallArgsFromVp(argc, vp); 6511 6512 // Step 1. Let promise be the this value. 6513 HandleValue thisVal = args.thisv(); 6514 HandleValue onFulfilled = UndefinedHandleValue; 6515 HandleValue onRejected = args.get(0); 6516 6517 // Fast path when the default Promise state is intact. 6518 if (CanCallOriginalPromiseThenBuiltin(cx, thisVal)) { 6519 return OriginalPromiseThenBuiltin(cx, thisVal, onFulfilled, onRejected, 6520 args.rval(), rvalExplicitlyUsed); 6521 } 6522 6523 // Step 2. Return ? Invoke(promise, "then", « undefined, onRejected »). 6524 RootedValue thenVal(cx); 6525 RootedObject thisObj(cx, ToObject(cx, thisVal)); 6526 bool isOnProto = false; 6527 bool isOnStandardProto = false; 6528 bool isOnObjectProto = false; 6529 if (!thisObj) { 6530 return false; 6531 } 6532 if (!GetThenValue(cx, thisObj, thisVal, &thenVal, &isOnProto, 6533 &isOnStandardProto, &isOnObjectProto)) { 6534 return false; 6535 } 6536 6537 if (IsNativeFunction(thenVal, &Promise_then) && 6538 thenVal.toObject().nonCCWRealm() == cx->realm()) { 6539 return Promise_then_impl(cx, thisVal, onFulfilled, onRejected, args.rval(), 6540 rvalExplicitlyUsed); 6541 } 6542 6543 ReportThenable(cx, isOnProto, isOnStandardProto, isOnObjectProto); 6544 return Call(cx, thenVal, thisVal, UndefinedHandleValue, onRejected, 6545 args.rval()); 6546 } 6547 6548 /** 6549 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6550 * 6551 * Promise.prototype.catch ( onRejected ) 6552 * https://tc39.es/ecma262/#sec-promise.prototype.catch 6553 */ 6554 static bool Promise_catch_noRetVal(JSContext* cx, unsigned argc, Value* vp) { 6555 return Promise_catch_impl(cx, argc, vp, false); 6556 } 6557 6558 /** 6559 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6560 * 6561 * Promise.prototype.catch ( onRejected ) 6562 * https://tc39.es/ecma262/#sec-promise.prototype.catch 6563 */ 6564 static bool Promise_catch(JSContext* cx, unsigned argc, Value* vp) { 6565 return Promise_catch_impl(cx, argc, vp, true); 6566 } 6567 6568 /** 6569 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6570 * 6571 * Promise.prototype.then ( onFulfilled, onRejected ) 6572 * https://tc39.es/ecma262/#sec-promise.prototype.then 6573 */ 6574 static bool Promise_then_impl(JSContext* cx, HandleValue promiseVal, 6575 HandleValue onFulfilled, HandleValue onRejected, 6576 MutableHandleValue rval, 6577 bool rvalExplicitlyUsed) { 6578 // Step 1. Let promise be the this value. 6579 // (implicit) 6580 6581 // Step 2. If IsPromise(promise) is false, throw a TypeError exception. 6582 if (!promiseVal.isObject()) { 6583 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 6584 JSMSG_OBJECT_REQUIRED, 6585 "Receiver of Promise.prototype.then call"); 6586 return false; 6587 } 6588 6589 // Fast path when the default Promise state is intact. 6590 if (CanCallOriginalPromiseThenBuiltin(cx, promiseVal)) { 6591 // Steps 3-5. 6592 return OriginalPromiseThenBuiltin(cx, promiseVal, onFulfilled, onRejected, 6593 rval, rvalExplicitlyUsed); 6594 } 6595 6596 RootedObject promiseObj(cx, &promiseVal.toObject()); 6597 Rooted<PromiseObject*> unwrappedPromise( 6598 cx, 6599 UnwrapAndTypeCheckValue<PromiseObject>(cx, promiseVal, [cx, &promiseVal] { 6600 JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, 6601 JSMSG_INCOMPATIBLE_PROTO, "Promise", "then", 6602 InformalValueTypeName(promiseVal)); 6603 })); 6604 if (!unwrappedPromise) { 6605 return false; 6606 } 6607 6608 bool rvalUsed = 6609 rvalExplicitlyUsed || 6610 IsPromiseThenOrCatchRetValImplicitlyUsed(cx, unwrappedPromise); 6611 6612 // Step 3. Let C be ? SpeciesConstructor(promise, %Promise%). 6613 // Step 4. Let resultCapability be ? NewPromiseCapability(C). 6614 CreateDependentPromise createDependent = 6615 rvalUsed ? CreateDependentPromise::Always 6616 : CreateDependentPromise::SkipIfCtorUnobservable; 6617 Rooted<PromiseCapability> resultCapability(cx); 6618 if (!PromiseThenNewPromiseCapability(cx, promiseObj, createDependent, 6619 &resultCapability)) { 6620 return false; 6621 } 6622 6623 // Step 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, 6624 // resultCapability). 6625 if (!PerformPromiseThen(cx, unwrappedPromise, onFulfilled, onRejected, 6626 resultCapability)) { 6627 return false; 6628 } 6629 6630 if (rvalUsed) { 6631 rval.setObject(*resultCapability.promise()); 6632 } else { 6633 rval.setUndefined(); 6634 } 6635 return true; 6636 } 6637 6638 /** 6639 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6640 * 6641 * Promise.prototype.then ( onFulfilled, onRejected ) 6642 * https://tc39.es/ecma262/#sec-promise.prototype.then 6643 */ 6644 bool Promise_then_noRetVal(JSContext* cx, unsigned argc, Value* vp) { 6645 CallArgs args = CallArgsFromVp(argc, vp); 6646 return Promise_then_impl(cx, args.thisv(), args.get(0), args.get(1), 6647 args.rval(), false); 6648 } 6649 6650 /** 6651 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6652 * 6653 * Promise.prototype.then ( onFulfilled, onRejected ) 6654 * https://tc39.es/ecma262/#sec-promise.prototype.then 6655 */ 6656 bool js::Promise_then(JSContext* cx, unsigned argc, Value* vp) { 6657 CallArgs args = CallArgsFromVp(argc, vp); 6658 return Promise_then_impl(cx, args.thisv(), args.get(0), args.get(1), 6659 args.rval(), true); 6660 } 6661 6662 /** 6663 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6664 * 6665 * PerformPromiseThen ( promise, onFulfilled, onRejected 6666 * [ , resultCapability ] ) 6667 * https://tc39.es/ecma262/#sec-performpromisethen 6668 * 6669 * Steps 1-12. 6670 */ 6671 [[nodiscard]] static bool PerformPromiseThen( 6672 JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled_, 6673 HandleValue onRejected_, Handle<PromiseCapability> resultCapability) { 6674 // Step 1. Assert: IsPromise(promise) is true. 6675 // Step 2. If resultCapability is not present, then 6676 // Step 2. a. Set resultCapability to undefined. 6677 // (implicit) 6678 6679 // (reordered) 6680 // Step 4. Else, 6681 // Step 4. a. Let onFulfilledJobCallback be HostMakeJobCallback(onFulfilled). 6682 RootedValue onFulfilled(cx, onFulfilled_); 6683 6684 // Step 3. If IsCallable(onFulfilled) is false, then 6685 if (!IsCallable(onFulfilled)) { 6686 // Step 3. a. Let onFulfilledJobCallback be empty. 6687 onFulfilled = Int32Value(int32_t(PromiseHandler::Identity)); 6688 } 6689 6690 // (reordered) 6691 // Step 6. Else, 6692 // Step 6. a. Let onRejectedJobCallback be HostMakeJobCallback(onRejected). 6693 RootedValue onRejected(cx, onRejected_); 6694 6695 // Step 5. If IsCallable(onRejected) is false, then 6696 if (!IsCallable(onRejected)) { 6697 // Step 5. a. Let onRejectedJobCallback be empty. 6698 onRejected = Int32Value(int32_t(PromiseHandler::Thrower)); 6699 } 6700 6701 // Step 7. Let fulfillReaction be the PromiseReaction 6702 // { [[Capability]]: resultCapability, [[Type]]: Fulfill, 6703 // [[Handler]]: onFulfilledJobCallback }. 6704 // Step 8. Let rejectReaction be the PromiseReaction 6705 // { [[Capability]]: resultCapability, [[Type]]: Reject, 6706 // [[Handler]]: onRejectedJobCallback }. 6707 // 6708 // NOTE: We use single object for both reactions. 6709 auto hostDefinedDataObjectOption = 6710 promise->state() == JS::PromiseState::Pending 6711 ? HostDefinedDataObjectOption::Allocate 6712 : HostDefinedDataObjectOption::OptimizeOut; 6713 Rooted<PromiseReactionRecord*> reaction( 6714 cx, NewReactionRecord(cx, resultCapability, onFulfilled, onRejected, 6715 hostDefinedDataObjectOption)); 6716 if (!reaction) { 6717 return false; 6718 } 6719 6720 // Steps 9-14. 6721 return PerformPromiseThenWithReaction(cx, promise, reaction); 6722 } 6723 6724 /** 6725 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6726 * 6727 * PerformPromiseThen ( promise, onFulfilled, onRejected 6728 * [ , resultCapability ] ) 6729 * https://tc39.es/ecma262/#sec-performpromisethen 6730 */ 6731 [[nodiscard]] static bool PerformPromiseThenWithoutSettleHandlers( 6732 JSContext* cx, Handle<PromiseObject*> promise, 6733 Handle<PromiseObject*> promiseToResolve, 6734 Handle<PromiseCapability> resultCapability) { 6735 // Step 1. Assert: IsPromise(promise) is true. 6736 // Step 2. If resultCapability is not present, then 6737 // (implicit) 6738 6739 // Step 3. If IsCallable(onFulfilled) is false, then 6740 // Step 3.a. Let onFulfilledJobCallback be empty. 6741 HandleValue onFulfilled = NullHandleValue; 6742 6743 // Step 5. If IsCallable(onRejected) is false, then 6744 // Step 5.a. Let onRejectedJobCallback be empty. 6745 HandleValue onRejected = NullHandleValue; 6746 6747 // When the promise's state isn't pending, the embedding 6748 // should be able to retrieve the host defined object 6749 // on their own, so here we optimize out from the 6750 // our side. 6751 auto hostDefinedDataObjectOption = 6752 promise->state() == JS::PromiseState::Pending 6753 ? HostDefinedDataObjectOption::Allocate 6754 : HostDefinedDataObjectOption::OptimizeOut; 6755 6756 // Step 7. Let fulfillReaction be the PromiseReaction 6757 // { [[Capability]]: resultCapability, [[Type]]: Fulfill, 6758 // [[Handler]]: onFulfilledJobCallback }. 6759 // Step 8. Let rejectReaction be the PromiseReaction 6760 // { [[Capability]]: resultCapability, [[Type]]: Reject, 6761 // [[Handler]]: onRejectedJobCallback }. 6762 Rooted<PromiseReactionRecord*> reaction( 6763 cx, NewReactionRecord(cx, resultCapability, onFulfilled, onRejected, 6764 hostDefinedDataObjectOption)); 6765 if (!reaction) { 6766 return false; 6767 } 6768 6769 reaction->setIsDefaultResolvingHandler(promiseToResolve); 6770 6771 // Steps 9-12. 6772 return PerformPromiseThenWithReaction(cx, promise, reaction); 6773 } 6774 6775 /** 6776 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6777 * 6778 * PerformPromiseThen ( promise, onFulfilled, onRejected 6779 * [ , resultCapability ] ) 6780 * https://tc39.github.io/ecma262/#sec-performpromisethen 6781 * 6782 * Steps 9-12. 6783 */ 6784 [[nodiscard]] static bool PerformPromiseThenWithReaction( 6785 JSContext* cx, Handle<PromiseObject*> unwrappedPromise, 6786 Handle<PromiseReactionRecord*> reaction) { 6787 // Step 9. If promise.[[PromiseState]] is pending, then 6788 JS::PromiseState state = unwrappedPromise->state(); 6789 int32_t flags = unwrappedPromise->flags(); 6790 if (state == JS::PromiseState::Pending) { 6791 // Step 9.a. Append fulfillReaction as the last element of the List that is 6792 // promise.[[PromiseFulfillReactions]]. 6793 // Step 9.b. Append rejectReaction as the last element of the List that is 6794 // promise.[[PromiseRejectReactions]]. 6795 // 6796 // Instead of creating separate reaction records for fulfillment and 6797 // rejection, we create a combined record. All places we use the record 6798 // can handle that. 6799 if (!AddPromiseReaction(cx, unwrappedPromise, reaction)) { 6800 return false; 6801 } 6802 } 6803 6804 // Steps 10-11. 6805 else { 6806 // Step 11.a. Assert: The value of promise.[[PromiseState]] is rejected. 6807 MOZ_ASSERT_IF(state != JS::PromiseState::Fulfilled, 6808 state == JS::PromiseState::Rejected); 6809 6810 // Step 10.a. Let value be promise.[[PromiseResult]]. 6811 // Step 11.b. Let reason be promise.[[PromiseResult]]. 6812 RootedValue valueOrReason(cx, unwrappedPromise->valueOrReason()); 6813 6814 // We might be operating on a promise from another compartment. In that 6815 // case, we need to wrap the result/reason value before using it. 6816 if (!cx->compartment()->wrap(cx, &valueOrReason)) { 6817 return false; 6818 } 6819 6820 // Step 11.c. If promise.[[PromiseIsHandled]] is false, 6821 // perform HostPromiseRejectionTracker(promise, "handle"). 6822 if (state == JS::PromiseState::Rejected && 6823 !(flags & PROMISE_FLAG_HANDLED)) { 6824 cx->runtime()->removeUnhandledRejectedPromise(cx, unwrappedPromise); 6825 } 6826 6827 // Step 10.b. Let fulfillJob be 6828 // NewPromiseReactionJob(fulfillReaction, value). 6829 // Step 10.c. Perform HostEnqueuePromiseJob(fulfillJob.[[Job]], 6830 // fulfillJob.[[Realm]]). 6831 // Step 11.d. Let rejectJob be 6832 // NewPromiseReactionJob(rejectReaction, reason). 6833 // Step 11.e. Perform HostEnqueuePromiseJob(rejectJob.[[Job]], 6834 // rejectJob.[[Realm]]). 6835 if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state)) { 6836 return false; 6837 } 6838 } 6839 6840 // Step 12. Set promise.[[PromiseIsHandled]] to true. 6841 unwrappedPromise->setHandled(); 6842 6843 return true; 6844 } 6845 6846 /** 6847 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 6848 * 6849 * PerformPromiseThen ( promise, onFulfilled, onRejected 6850 * [ , resultCapability ] ) 6851 * https://tc39.github.io/ecma262/#sec-performpromisethen 6852 * 6853 * Steps 9.a-b. 6854 */ 6855 [[nodiscard]] static bool AddPromiseReaction( 6856 JSContext* cx, Handle<PromiseObject*> unwrappedPromise, 6857 Handle<PromiseReactionRecord*> reaction) { 6858 MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>()); 6859 RootedValue reactionVal(cx, ObjectValue(*reaction)); 6860 6861 // The code that creates Promise reactions can handle wrapped Promises, 6862 // unwrapping them as needed. That means that the `promise` and `reaction` 6863 // objects we have here aren't necessarily from the same compartment. In 6864 // order to store the reaction on the promise, we have to ensure that it 6865 // is properly wrapped. 6866 mozilla::Maybe<AutoRealm> ar; 6867 if (unwrappedPromise->compartment() != cx->compartment()) { 6868 ar.emplace(cx, unwrappedPromise); 6869 if (!cx->compartment()->wrap(cx, &reactionVal)) { 6870 return false; 6871 } 6872 } 6873 Handle<PromiseObject*> promise = unwrappedPromise; 6874 6875 // Step 9.a. Append fulfillReaction as the last element of the List that is 6876 // promise.[[PromiseFulfillReactions]]. 6877 // Step 9.b. Append rejectReaction as the last element of the List that is 6878 // promise.[[PromiseRejectReactions]]. 6879 RootedValue reactionsVal(cx, promise->reactions()); 6880 6881 if (reactionsVal.isUndefined()) { 6882 // If no reactions existed so far, just store the reaction record directly. 6883 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, reactionVal); 6884 return true; 6885 } 6886 6887 RootedObject reactionsObj(cx, &reactionsVal.toObject()); 6888 6889 // If only a single reaction exists, it's stored directly instead of in a 6890 // list. In that case, `reactionsObj` might be a wrapper, which we can 6891 // always safely unwrap. 6892 if (IsProxy(reactionsObj)) { 6893 reactionsObj = UncheckedUnwrap(reactionsObj); 6894 if (JS_IsDeadWrapper(reactionsObj)) { 6895 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 6896 JSMSG_DEAD_OBJECT); 6897 return false; 6898 } 6899 MOZ_RELEASE_ASSERT(reactionsObj->is<PromiseReactionRecord>()); 6900 } 6901 6902 if (reactionsObj->is<PromiseReactionRecord>()) { 6903 // If a single reaction existed so far, create a list and store the 6904 // old and the new reaction in it. 6905 ArrayObject* reactions = NewDenseFullyAllocatedArray(cx, 2); 6906 if (!reactions) { 6907 return false; 6908 } 6909 6910 reactions->setDenseInitializedLength(2); 6911 reactions->initDenseElement(0, reactionsVal); 6912 reactions->initDenseElement(1, reactionVal); 6913 6914 promise->setFixedSlot(PromiseSlot_ReactionsOrResult, 6915 ObjectValue(*reactions)); 6916 } else { 6917 // Otherwise, just store the new reaction. 6918 MOZ_RELEASE_ASSERT(reactionsObj->is<NativeObject>()); 6919 Handle<NativeObject*> reactions = reactionsObj.as<NativeObject>(); 6920 uint32_t len = reactions->getDenseInitializedLength(); 6921 DenseElementResult result = reactions->ensureDenseElements(cx, len, 1); 6922 if (result != DenseElementResult::Success) { 6923 MOZ_ASSERT(result == DenseElementResult::Failure); 6924 return false; 6925 } 6926 reactions->setDenseElement(len, reactionVal); 6927 } 6928 6929 return true; 6930 } 6931 6932 [[nodiscard]] static bool AddDummyPromiseReactionForDebugger( 6933 JSContext* cx, Handle<PromiseObject*> promise, 6934 HandleObject dependentPromise) { 6935 if (promise->state() != JS::PromiseState::Pending) { 6936 return true; 6937 } 6938 6939 if (JS_IsDeadWrapper(UncheckedUnwrap(dependentPromise))) { 6940 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 6941 return false; 6942 } 6943 6944 // `dependentPromise` should be a maybe-wrapped Promise. 6945 MOZ_ASSERT(UncheckedUnwrap(dependentPromise)->is<PromiseObject>()); 6946 6947 // Leave resolve and reject as null. 6948 Rooted<PromiseCapability> capability(cx); 6949 capability.promise().set(dependentPromise); 6950 6951 Rooted<PromiseReactionRecord*> reaction( 6952 cx, NewReactionRecord(cx, capability, NullHandleValue, NullHandleValue, 6953 HostDefinedDataObjectOption::UnusedForDebugger)); 6954 if (!reaction) { 6955 return false; 6956 } 6957 6958 reaction->setIsDebuggerDummy(); 6959 6960 return AddPromiseReaction(cx, promise, reaction); 6961 } 6962 6963 uint64_t PromiseObject::getID() { return PromiseDebugInfo::id(this); } 6964 6965 double PromiseObject::lifetime() { 6966 return MillisecondsSinceStartup() - allocationTime(); 6967 } 6968 6969 /** 6970 * Returns all promises that directly depend on this one. That means those 6971 * created by calling `then` on this promise, or the promise returned by 6972 * `Promise.all(iterable)` or `Promise.race(iterable)`, with this promise 6973 * being a member of the passed-in `iterable`. 6974 * 6975 * Per spec, we should have separate lists of reaction records for the 6976 * fulfill and reject cases. As an optimization, we have only one of those, 6977 * containing the required data for both cases. So we just walk that list 6978 * and extract the dependent promises from all reaction records. 6979 */ 6980 bool PromiseObject::dependentPromises(JSContext* cx, 6981 MutableHandle<GCVector<Value>> values) { 6982 if (state() != JS::PromiseState::Pending) { 6983 return true; 6984 } 6985 6986 uint32_t valuesIndex = 0; 6987 RootedValue reactionsVal(cx, reactions()); 6988 6989 return ForEachReaction(cx, reactionsVal, [&](MutableHandleObject obj) { 6990 if (IsProxy(obj)) { 6991 obj.set(UncheckedUnwrap(obj)); 6992 } 6993 6994 if (JS_IsDeadWrapper(obj)) { 6995 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 6996 JSMSG_DEAD_OBJECT); 6997 return false; 6998 } 6999 7000 MOZ_RELEASE_ASSERT(obj->is<PromiseReactionRecord>()); 7001 auto* reaction = &obj->as<PromiseReactionRecord>(); 7002 7003 // Not all reactions have a Promise on them. 7004 JSObject* promiseObj = reaction->promise(); 7005 if (promiseObj) { 7006 if (!values.growBy(1)) { 7007 return false; 7008 } 7009 7010 values[valuesIndex++].setObject(*promiseObj); 7011 } 7012 return true; 7013 }); 7014 } 7015 7016 bool PromiseObject::forEachReactionRecord( 7017 JSContext* cx, PromiseReactionRecordBuilder& builder) { 7018 if (state() != JS::PromiseState::Pending) { 7019 // Promise was resolved, so no reaction records are present. 7020 return true; 7021 } 7022 7023 RootedValue reactionsVal(cx, reactions()); 7024 if (reactionsVal.isNullOrUndefined()) { 7025 // No reaction records are attached to this promise. 7026 return true; 7027 } 7028 7029 return ForEachReaction(cx, reactionsVal, [&](MutableHandleObject obj) { 7030 if (IsProxy(obj)) { 7031 obj.set(UncheckedUnwrap(obj)); 7032 } 7033 7034 if (JS_IsDeadWrapper(obj)) { 7035 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 7036 JSMSG_DEAD_OBJECT); 7037 return false; 7038 } 7039 7040 Rooted<PromiseReactionRecord*> reaction(cx, 7041 &obj->as<PromiseReactionRecord>()); 7042 MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending); 7043 7044 if (reaction->isAsyncFunction()) { 7045 Rooted<AsyncFunctionGeneratorObject*> generator( 7046 cx, reaction->asyncFunctionGenerator()); 7047 if (!builder.asyncFunction(cx, generator)) { 7048 return false; 7049 } 7050 } else if (reaction->isAsyncGenerator()) { 7051 Rooted<AsyncGeneratorObject*> generator(cx, reaction->asyncGenerator()); 7052 if (!builder.asyncGenerator(cx, generator)) { 7053 return false; 7054 } 7055 } else if (reaction->isDefaultResolvingHandler()) { 7056 Rooted<PromiseObject*> promise(cx, reaction->defaultResolvingPromise()); 7057 if (!builder.direct(cx, promise)) { 7058 return false; 7059 } 7060 } else { 7061 RootedObject resolve(cx); 7062 RootedObject reject(cx); 7063 RootedObject result(cx, reaction->promise()); 7064 7065 Value v = reaction->getFixedSlot(PromiseReactionRecord::OnFulfilled); 7066 if (v.isObject()) { 7067 resolve = &v.toObject(); 7068 } 7069 7070 v = reaction->getFixedSlot(PromiseReactionRecord::OnRejected); 7071 if (v.isObject()) { 7072 reject = &v.toObject(); 7073 } 7074 7075 if (!builder.then(cx, resolve, reject, result)) { 7076 return false; 7077 } 7078 } 7079 7080 return true; 7081 }); 7082 } 7083 7084 /** 7085 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 7086 * 7087 * Promise Reject Functions 7088 * https://tc39.es/ecma262/#sec-promise-reject-functions 7089 */ 7090 static bool CallDefaultPromiseResolveFunction(JSContext* cx, 7091 Handle<PromiseObject*> promise, 7092 HandleValue resolutionValue) { 7093 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 7094 7095 // Steps 1-3. 7096 // (implicit) 7097 7098 // Step 4. Let alreadyResolved be F.[[AlreadyResolved]]. 7099 // Step 5. If alreadyResolved.[[Value]] is true, return undefined. 7100 if (IsAlreadyResolvedPromiseWithDefaultResolvingFunction(promise)) { 7101 return true; 7102 } 7103 7104 // Step 6. Set alreadyResolved.[[Value]] to true. 7105 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise); 7106 7107 // Steps 7-15. 7108 // (implicit) Step 16. Return undefined. 7109 return ResolvePromiseInternal(cx, promise, resolutionValue); 7110 } 7111 7112 /* static */ 7113 bool PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, 7114 HandleValue resolutionValue) { 7115 MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC)); 7116 if (promise->state() != JS::PromiseState::Pending) { 7117 return true; 7118 } 7119 7120 if (IsPromiseWithDefaultResolvingFunction(promise)) { 7121 return CallDefaultPromiseResolveFunction(cx, promise, resolutionValue); 7122 } 7123 7124 JSFunction* resolveFun = GetResolveFunctionFromPromise(promise); 7125 if (!resolveFun) { 7126 return true; 7127 } 7128 7129 RootedValue funVal(cx, ObjectValue(*resolveFun)); 7130 7131 // For xray'd Promises, the resolve fun may have been created in another 7132 // compartment. For the call below to work in that case, wrap the 7133 // function into the current compartment. 7134 if (!cx->compartment()->wrap(cx, &funVal)) { 7135 return false; 7136 } 7137 7138 RootedValue dummy(cx); 7139 return Call(cx, funVal, UndefinedHandleValue, resolutionValue, &dummy); 7140 } 7141 7142 /** 7143 * ES2023 draft rev 714fa3dd1e8237ae9c666146270f81880089eca5 7144 * 7145 * Promise Reject Functions 7146 * https://tc39.es/ecma262/#sec-promise-reject-functions 7147 */ 7148 static bool CallDefaultPromiseRejectFunction( 7149 JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue, 7150 JS::Handle<SavedFrame*> unwrappedRejectionStack /* = nullptr */) { 7151 MOZ_ASSERT(IsPromiseWithDefaultResolvingFunction(promise)); 7152 7153 // Steps 1-3. 7154 // (implicit) 7155 7156 // Step 4. Let alreadyResolved be F.[[AlreadyResolved]]. 7157 // Step 5. If alreadyResolved.[[Value]] is true, return undefined. 7158 if (IsAlreadyResolvedPromiseWithDefaultResolvingFunction(promise)) { 7159 return true; 7160 } 7161 7162 // Step 6. Set alreadyResolved.[[Value]] to true. 7163 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise); 7164 7165 return RejectPromiseInternal(cx, promise, rejectionValue, 7166 unwrappedRejectionStack); 7167 } 7168 7169 /* static */ 7170 bool PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, 7171 HandleValue rejectionValue) { 7172 MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC)); 7173 if (promise->state() != JS::PromiseState::Pending) { 7174 return true; 7175 } 7176 7177 if (IsPromiseWithDefaultResolvingFunction(promise)) { 7178 return CallDefaultPromiseRejectFunction(cx, promise, rejectionValue); 7179 } 7180 7181 RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction)); 7182 MOZ_ASSERT(IsCallable(funVal)); 7183 7184 RootedValue dummy(cx); 7185 return Call(cx, funVal, UndefinedHandleValue, rejectionValue, &dummy); 7186 } 7187 7188 /** 7189 * ES2022 draft rev d03c1ec6e235a5180fa772b6178727c17974cb14 7190 * 7191 * RejectPromise ( promise, reason ) 7192 * https://tc39.es/ecma262/#sec-rejectpromise 7193 * 7194 * Step 7. 7195 */ 7196 /* static */ 7197 void PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise, 7198 Handle<SavedFrame*> unwrappedRejectionStack) { 7199 PromiseDebugInfo::setResolutionInfo(cx, promise, unwrappedRejectionStack); 7200 7201 // Step 7. If promise.[[PromiseIsHandled]] is false, perform 7202 // HostPromiseRejectionTracker(promise, "reject"). 7203 if (promise->state() == JS::PromiseState::Rejected && 7204 promise->isUnhandled()) { 7205 cx->runtime()->addUnhandledRejectedPromise(cx, promise); 7206 } 7207 7208 DebugAPI::onPromiseSettled(cx, promise); 7209 } 7210 7211 void PromiseObject::setRequiresUserInteractionHandling(bool state) { 7212 if (state) { 7213 AddPromiseFlags(*this, PROMISE_FLAG_REQUIRES_USER_INTERACTION_HANDLING); 7214 } else { 7215 RemovePromiseFlags(*this, PROMISE_FLAG_REQUIRES_USER_INTERACTION_HANDLING); 7216 } 7217 } 7218 7219 void PromiseObject::setHadUserInteractionUponCreation(bool state) { 7220 if (state) { 7221 AddPromiseFlags(*this, PROMISE_FLAG_HAD_USER_INTERACTION_UPON_CREATION); 7222 } else { 7223 RemovePromiseFlags(*this, PROMISE_FLAG_HAD_USER_INTERACTION_UPON_CREATION); 7224 } 7225 } 7226 7227 void PromiseObject::copyUserInteractionFlagsFrom(PromiseObject& rhs) { 7228 setRequiresUserInteractionHandling(rhs.requiresUserInteractionHandling()); 7229 setHadUserInteractionUponCreation(rhs.hadUserInteractionUponCreation()); 7230 } 7231 7232 #if defined(DEBUG) || defined(JS_JITSPEW) 7233 void PromiseDebugInfo::dumpOwnFields(js::JSONPrinter& json) const { 7234 if (getFixedSlot(Slot_Id).isNumber()) { 7235 json.formatProperty("id", "%lf", getFixedSlot(Slot_Id).toNumber()); 7236 } 7237 7238 if (getFixedSlot(Slot_AllocationTime).isNumber()) { 7239 json.formatProperty("allocationTime", "%lf", 7240 getFixedSlot(Slot_AllocationTime).toNumber()); 7241 } 7242 7243 { 7244 js::GenericPrinter& out = json.beginStringProperty("allocationSite"); 7245 getFixedSlot(Slot_AllocationSite).dumpStringContent(out); 7246 json.endStringProperty(); 7247 } 7248 7249 if (getFixedSlot(Slot_ResolutionTime).isNumber()) { 7250 json.formatProperty("resolutionTime", "%lf", 7251 getFixedSlot(Slot_ResolutionTime).toNumber()); 7252 } 7253 7254 { 7255 js::GenericPrinter& out = json.beginStringProperty("resolutionSite"); 7256 getFixedSlot(Slot_ResolutionSite).dumpStringContent(out); 7257 json.endStringProperty(); 7258 } 7259 } 7260 7261 template <typename KnownF, typename UnknownF> 7262 /* static */ 7263 void PromiseReactionRecord::forEachReactionFlag(uint32_t flags, KnownF known, 7264 UnknownF unknown) { 7265 for (uint32_t i = 1; i; i = i << 1) { 7266 if (!(flags & i)) { 7267 continue; 7268 } 7269 switch (flags & i) { 7270 case REACTION_FLAG_RESOLVED: 7271 known("RESOLVED"); 7272 break; 7273 case REACTION_FLAG_FULFILLED: 7274 known("FULFILLED"); 7275 break; 7276 case REACTION_FLAG_DEFAULT_RESOLVING_HANDLER: 7277 known("DEFAULT_RESOLVING_HANDLER"); 7278 break; 7279 case REACTION_FLAG_ASYNC_FUNCTION: 7280 known("ASYNC_FUNCTION"); 7281 break; 7282 case REACTION_FLAG_ASYNC_GENERATOR: 7283 known("ASYNC_GENERATOR"); 7284 break; 7285 case REACTION_FLAG_DEBUGGER_DUMMY: 7286 known("DEBUGGER_DUMMY"); 7287 break; 7288 case REACTION_FLAG_IGNORE_UNHANDLED_REJECTION: 7289 known("IGNORE_UNHANDLED_REJECTION"); 7290 break; 7291 default: 7292 unknown(i); 7293 break; 7294 } 7295 } 7296 } 7297 7298 void PromiseReactionRecord::dumpOwnFields(js::JSONPrinter& json) const { 7299 if (promise()) { 7300 js::GenericPrinter& out = json.beginStringProperty("promise"); 7301 promise()->dumpStringContent(out); 7302 json.endStringProperty(); 7303 } 7304 7305 if (targetState() == JS::PromiseState::Fulfilled) { 7306 { 7307 js::GenericPrinter& out = json.beginStringProperty("onFulfilled"); 7308 getFixedSlot(OnFulfilled).dumpStringContent(out); 7309 json.endStringProperty(); 7310 } 7311 { 7312 js::GenericPrinter& out = json.beginStringProperty("onFulfilledArg"); 7313 getFixedSlot(OnFulfilledArg).dumpStringContent(out); 7314 json.endStringProperty(); 7315 } 7316 } 7317 7318 if (targetState() == JS::PromiseState::Rejected) { 7319 { 7320 js::GenericPrinter& out = json.beginStringProperty("onRejected"); 7321 getFixedSlot(OnRejected).dumpStringContent(out); 7322 json.endStringProperty(); 7323 } 7324 { 7325 js::GenericPrinter& out = json.beginStringProperty("onRejectedArg"); 7326 getFixedSlot(OnRejectedArg).dumpStringContent(out); 7327 json.endStringProperty(); 7328 } 7329 } 7330 7331 if (!getFixedSlot(Resolve).isNull()) { 7332 js::GenericPrinter& out = json.beginStringProperty("resolve"); 7333 getFixedSlot(Resolve).dumpStringContent(out); 7334 json.endStringProperty(); 7335 } 7336 7337 if (!getFixedSlot(Reject).isNull()) { 7338 js::GenericPrinter& out = json.beginStringProperty("reject"); 7339 getFixedSlot(Reject).dumpStringContent(out); 7340 json.endStringProperty(); 7341 } 7342 7343 { 7344 js::GenericPrinter& out = json.beginStringProperty("hostDefinedData"); 7345 getFixedSlot(HostDefinedData).dumpStringContent(out); 7346 json.endStringProperty(); 7347 } 7348 7349 json.beginInlineListProperty("flags"); 7350 forEachReactionFlag( 7351 flags(), [&](const char* name) { json.value("%s", name); }, 7352 [&](uint32_t value) { json.value("Unknown(%08x)", value); }); 7353 json.endInlineList(); 7354 7355 if (isDefaultResolvingHandler()) { 7356 js::GenericPrinter& out = json.beginStringProperty("promiseToResolve"); 7357 getFixedSlot(GeneratorOrPromiseToResolveOrAsyncFromSyncIterator) 7358 .dumpStringContent(out); 7359 json.endStringProperty(); 7360 } 7361 7362 if (isAsyncFunction()) { 7363 js::GenericPrinter& out = json.beginStringProperty("generator"); 7364 getFixedSlot(GeneratorOrPromiseToResolveOrAsyncFromSyncIterator) 7365 .dumpStringContent(out); 7366 json.endStringProperty(); 7367 } 7368 7369 if (isAsyncGenerator()) { 7370 js::GenericPrinter& out = json.beginStringProperty("generator"); 7371 getFixedSlot(GeneratorOrPromiseToResolveOrAsyncFromSyncIterator) 7372 .dumpStringContent(out); 7373 json.endStringProperty(); 7374 } 7375 } 7376 7377 void DumpReactions(js::JSONPrinter& json, const JS::Value& reactionsVal) { 7378 if (reactionsVal.isUndefined()) { 7379 return; 7380 } 7381 7382 if (reactionsVal.isObject()) { 7383 JSObject* reactionsObj = &reactionsVal.toObject(); 7384 if (IsProxy(reactionsObj)) { 7385 reactionsObj = UncheckedUnwrap(reactionsObj); 7386 } 7387 7388 if (reactionsObj->is<PromiseReactionRecord>()) { 7389 json.beginObject(); 7390 reactionsObj->as<PromiseReactionRecord>().dumpOwnFields(json); 7391 json.endObject(); 7392 return; 7393 } 7394 7395 if (reactionsObj->is<NativeObject>()) { 7396 NativeObject* reactionsList = &reactionsObj->as<NativeObject>(); 7397 uint32_t len = reactionsList->getDenseInitializedLength(); 7398 for (uint32_t i = 0; i < len; i++) { 7399 const JS::Value& reactionVal = reactionsList->getDenseElement(i); 7400 if (reactionVal.isObject()) { 7401 JSObject* reactionsObj = &reactionVal.toObject(); 7402 if (IsProxy(reactionsObj)) { 7403 reactionsObj = UncheckedUnwrap(reactionsObj); 7404 } 7405 7406 if (reactionsObj->is<PromiseReactionRecord>()) { 7407 json.beginObject(); 7408 reactionsObj->as<PromiseReactionRecord>().dumpOwnFields(json); 7409 json.endObject(); 7410 continue; 7411 } 7412 } 7413 7414 js::GenericPrinter& out = json.beginString(); 7415 out.put("Unknown("); 7416 reactionVal.dumpStringContent(out); 7417 out.put(")"); 7418 json.endString(); 7419 } 7420 return; 7421 } 7422 } 7423 7424 js::GenericPrinter& out = json.beginString(); 7425 out.put("Unknown("); 7426 reactionsVal.dumpStringContent(out); 7427 out.put(")"); 7428 json.endString(); 7429 } 7430 7431 template <typename KnownF, typename UnknownF> 7432 void ForEachPromiseFlag(uint32_t flags, KnownF known, UnknownF unknown) { 7433 for (uint32_t i = 1; i; i = i << 1) { 7434 if (!(flags & i)) { 7435 continue; 7436 } 7437 switch (flags & i) { 7438 case PROMISE_FLAG_RESOLVED: 7439 known("RESOLVED"); 7440 break; 7441 case PROMISE_FLAG_FULFILLED: 7442 known("FULFILLED"); 7443 break; 7444 case PROMISE_FLAG_HANDLED: 7445 known("HANDLED"); 7446 break; 7447 case PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS: 7448 known("DEFAULT_RESOLVING_FUNCTIONS"); 7449 break; 7450 case PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS_ALREADY_RESOLVED: 7451 known("DEFAULT_RESOLVING_FUNCTIONS_ALREADY_RESOLVED"); 7452 break; 7453 case PROMISE_FLAG_ASYNC: 7454 known("ASYNC"); 7455 break; 7456 case PROMISE_FLAG_REQUIRES_USER_INTERACTION_HANDLING: 7457 known("REQUIRES_USER_INTERACTION_HANDLING"); 7458 break; 7459 case PROMISE_FLAG_HAD_USER_INTERACTION_UPON_CREATION: 7460 known("HAD_USER_INTERACTION_UPON_CREATION"); 7461 break; 7462 default: 7463 unknown(i); 7464 break; 7465 } 7466 } 7467 } 7468 7469 void PromiseObject::dumpOwnFields(js::JSONPrinter& json) const { 7470 json.beginInlineListProperty("flags"); 7471 ForEachPromiseFlag( 7472 flags(), [&](const char* name) { json.value("%s", name); }, 7473 [&](uint32_t value) { json.value("Unknown(%08x)", value); }); 7474 json.endInlineList(); 7475 7476 if (state() == JS::PromiseState::Pending) { 7477 json.property("state", "pending"); 7478 7479 json.beginListProperty("reactions"); 7480 DumpReactions(json, reactions()); 7481 json.endList(); 7482 } else if (state() == JS::PromiseState::Fulfilled) { 7483 json.property("state", "fulfilled"); 7484 7485 json.beginObjectProperty("value"); 7486 value().dumpFields(json); 7487 json.endObject(); 7488 } else if (state() == JS::PromiseState::Rejected) { 7489 json.property("state", "rejected"); 7490 7491 json.beginObjectProperty("reason"); 7492 reason().dumpFields(json); 7493 json.endObject(); 7494 } 7495 7496 JS::Value debugInfo = getFixedSlot(PromiseSlot_DebugInfo); 7497 if (debugInfo.isNumber()) { 7498 json.formatProperty("id", "%lf", debugInfo.toNumber()); 7499 } else if (debugInfo.isObject() && 7500 debugInfo.toObject().is<PromiseDebugInfo>()) { 7501 debugInfo.toObject().as<PromiseDebugInfo>().dumpOwnFields(json); 7502 } 7503 } 7504 7505 void PromiseObject::dumpOwnStringContent(js::GenericPrinter& out) const {} 7506 #endif 7507 7508 // We can skip `await` with an already resolved value only if the current frame 7509 // is the topmost JS frame and the current job is the last job in the job queue. 7510 // This guarantees that any new job enqueued in the current turn will be 7511 // executed immediately after the current job. 7512 // 7513 // Currently we only support skipping jobs when the async function is resumed 7514 // at least once. 7515 [[nodiscard]] static bool IsTopMostAsyncFunctionCall(JSContext* cx) { 7516 FrameIter iter(cx); 7517 7518 // The current frame should be the async function. 7519 if (iter.done()) { 7520 return false; 7521 } 7522 7523 if (!iter.isFunctionFrame() && iter.isModuleFrame()) { 7524 // The iterator is not a function frame, it is a module frame. 7525 // Ignore this optimization for now. 7526 return true; 7527 } 7528 7529 MOZ_ASSERT(iter.calleeTemplate()->isAsync()); 7530 7531 #ifdef DEBUG 7532 bool isGenerator = iter.calleeTemplate()->isGenerator(); 7533 #endif 7534 7535 ++iter; 7536 7537 // The parent frame should be the `next` function of the generator that is 7538 // internally called in AsyncFunctionResume resp. AsyncGeneratorResume. 7539 if (iter.done()) { 7540 return false; 7541 } 7542 // The initial call into an async function can happen from top-level code, so 7543 // the parent frame isn't required to be a function frame. Contrary to that, 7544 // the parent frame for an async generator function is always a function 7545 // frame, because async generators can't directly fall through to an `await` 7546 // expression from their initial call. 7547 if (!iter.isFunctionFrame()) { 7548 MOZ_ASSERT(!isGenerator); 7549 return false; 7550 } 7551 7552 // Always skip InterpretGeneratorResume if present. 7553 JSFunction* fun = iter.calleeTemplate(); 7554 if (IsSelfHostedFunctionWithName(fun, cx->names().InterpretGeneratorResume)) { 7555 ++iter; 7556 7557 if (iter.done()) { 7558 return false; 7559 } 7560 7561 MOZ_ASSERT(iter.isFunctionFrame()); 7562 fun = iter.calleeTemplate(); 7563 } 7564 7565 if (!IsSelfHostedFunctionWithName(fun, cx->names().AsyncFunctionNext) && 7566 !IsSelfHostedFunctionWithName(fun, cx->names().AsyncGeneratorNext)) { 7567 return false; 7568 } 7569 7570 ++iter; 7571 7572 // There should be no more frames. 7573 if (iter.done()) { 7574 return true; 7575 } 7576 7577 return false; 7578 } 7579 7580 [[nodiscard]] bool js::CanSkipAwait(JSContext* cx, HandleValue val, 7581 bool* canSkip) { 7582 if (!cx->canSkipEnqueuingJobs) { 7583 *canSkip = false; 7584 return true; 7585 } 7586 7587 if (!IsTopMostAsyncFunctionCall(cx)) { 7588 *canSkip = false; 7589 return true; 7590 } 7591 7592 // Primitive values cannot be 'thenables', so we can trivially skip the 7593 // await operation. 7594 if (!val.isObject()) { 7595 *canSkip = true; 7596 return true; 7597 } 7598 7599 JSObject* obj = &val.toObject(); 7600 if (!obj->is<PromiseObject>()) { 7601 *canSkip = false; 7602 return true; 7603 } 7604 7605 PromiseObject* promise = &obj->as<PromiseObject>(); 7606 7607 if (promise->state() == JS::PromiseState::Pending) { 7608 *canSkip = false; 7609 return true; 7610 } 7611 7612 if (!IsPromiseWithDefaultProperties(promise, cx)) { 7613 *canSkip = false; 7614 return true; 7615 } 7616 7617 if (promise->state() == JS::PromiseState::Rejected) { 7618 // We don't optimize rejected Promises for now. 7619 *canSkip = false; 7620 return true; 7621 } 7622 7623 *canSkip = true; 7624 return true; 7625 } 7626 7627 [[nodiscard]] bool js::ExtractAwaitValue(JSContext* cx, HandleValue val, 7628 MutableHandleValue resolved) { 7629 // Ensure all callers of this are jumping past the 7630 // extract if it's not possible to extract. 7631 #ifdef DEBUG 7632 bool canSkip; 7633 if (!CanSkipAwait(cx, val, &canSkip)) { 7634 return false; 7635 } 7636 MOZ_ASSERT(canSkip == true); 7637 #endif 7638 7639 // Primitive values cannot be 'thenables', so we can trivially skip the 7640 // await operation. 7641 if (!val.isObject()) { 7642 resolved.set(val); 7643 return true; 7644 } 7645 7646 JSObject* obj = &val.toObject(); 7647 PromiseObject* promise = &obj->as<PromiseObject>(); 7648 resolved.set(promise->value()); 7649 7650 return true; 7651 } 7652 7653 JS_PUBLIC_API bool JS::RunJSMicroTask(JSContext* cx, 7654 Handle<JS::JSMicroTask*> entry) { 7655 #ifdef DEBUG 7656 JSObject* global = JS::GetExecutionGlobalFromJSMicroTask(entry); 7657 MOZ_ASSERT_IF(global, global == cx->global()); 7658 #endif 7659 7660 RootedObject task(cx, entry); 7661 RootedObject unwrappedTask(cx, UncheckedUnwrap(entry)); 7662 if (JS_IsDeadWrapper(unwrappedTask)) { 7663 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); 7664 return false; 7665 } 7666 7667 if (unwrappedTask->is<PromiseReactionRecord>()) { 7668 // Note: We don't store a callback for promise reaction records because they 7669 // always call back into PromiseReactionJob. 7670 // 7671 // Note: We pass the (maybe)wrapped task here since PromiseReactionJob will 7672 // decide what realm to be in based on the wrapper if it exists. 7673 return PromiseReactionJob(cx, task); 7674 } 7675 7676 if (unwrappedTask->is<ThenableJob>()) { 7677 ThenableJob* job = &unwrappedTask->as<ThenableJob>(); 7678 ThenableJob::TargetFunction target = job->targetFunction(); 7679 7680 // MG:XXX: Note: Because we don't care about the result of these values 7681 // after the call, do these really have to be rooted (I don't think so?) 7682 RootedTuple<JSObject*, Value, JSObject*, JSObject*> roots(cx); 7683 RootedField<JSObject*, 0> promise(roots, job->promise()); 7684 RootedField<Value, 1> thenable(roots, job->thenable()); 7685 7686 switch (target) { 7687 case ThenableJob::PromiseResolveThenableJob: { 7688 // MG:XXX: Unify naming: is it `then` or `handler` make up your mind. 7689 RootedField<JSObject*, 3> then(roots, job->then()); 7690 return PromiseResolveThenableJob(cx, promise, thenable, then); 7691 } 7692 case ThenableJob::PromiseResolveBuiltinThenableJob: { 7693 RootedField<JSObject*, 2> thenableObj(roots, 7694 &job->thenable().toObject()); 7695 return PromiseResolveBuiltinThenableJob(cx, promise, thenableObj); 7696 } 7697 } 7698 MOZ_CRASH("Corrupted Target Function"); 7699 return false; 7700 } 7701 7702 MOZ_CRASH("Unknown Job type"); 7703 return false; 7704 } 7705 7706 template <> 7707 inline bool JSObject::is<MicroTaskEntry>() const { 7708 return is<ThenableJob>() || is<PromiseReactionRecord>(); 7709 } 7710 7711 JS_PUBLIC_API bool JS::MaybeGetHostDefinedDataFromJSMicroTask( 7712 JS::JSMicroTask* entry, MutableHandleObject out) { 7713 out.set(nullptr); 7714 JSObject* task = CheckedUnwrapStatic(entry); 7715 if (!task) { 7716 return false; 7717 } 7718 if (JS_IsDeadWrapper(task)) { 7719 return false; 7720 } 7721 7722 MOZ_ASSERT(task->is<MicroTaskEntry>()); 7723 JSObject* maybeHostDefined = 7724 task->as<MicroTaskEntry>().getHostDefinedData().toObjectOrNull(); 7725 7726 if (!maybeHostDefined) { 7727 return true; 7728 } 7729 7730 if (JS_IsDeadWrapper(maybeHostDefined)) { 7731 return false; 7732 } 7733 7734 JSObject* unwrapped = CheckedUnwrapStatic(maybeHostDefined); 7735 if (!unwrapped) { 7736 return false; 7737 } 7738 out.set(unwrapped); 7739 return true; 7740 } 7741 7742 JS_PUBLIC_API bool JS::MaybeGetAllocationSiteFromJSMicroTask( 7743 JS::JSMicroTask* entry, MutableHandleObject out) { 7744 JSObject* task = UncheckedUnwrap(entry); 7745 if (JS_IsDeadWrapper(task)) { 7746 return false; 7747 }; 7748 7749 MOZ_ASSERT(task->is<MicroTaskEntry>()); 7750 JSObject* maybeWrappedStack = task->as<MicroTaskEntry>().allocationStack(); 7751 7752 if (!maybeWrappedStack) { 7753 out.set(nullptr); 7754 return true; 7755 } 7756 7757 if (JS_IsDeadWrapper(maybeWrappedStack)) { 7758 return false; 7759 } 7760 7761 JSObject* unwrapped = UncheckedUnwrap(maybeWrappedStack); 7762 MOZ_ASSERT(unwrapped->is<SavedFrame>()); 7763 out.set(unwrapped); 7764 return true; 7765 } 7766 7767 JS_PUBLIC_API JSObject* JS::MaybeGetHostDefinedGlobalFromJSMicroTask( 7768 JSMicroTask* entry) { 7769 JSObject* task = UncheckedUnwrap(entry); 7770 if (JS_IsDeadWrapper(task)) { 7771 return nullptr; 7772 } 7773 7774 MOZ_ASSERT(task->is<MicroTaskEntry>()); 7775 7776 JSObject* maybeWrappedHostDefinedRepresentative = 7777 task->as<MicroTaskEntry>().hostDefinedGlobalRepresentative(); 7778 7779 if (maybeWrappedHostDefinedRepresentative) { 7780 JSObject* unwrapped = 7781 UncheckedUnwrap(maybeWrappedHostDefinedRepresentative); 7782 if (JS_IsDeadWrapper(unwrapped)) { 7783 return nullptr; 7784 } 7785 return &unwrapped->nonCCWGlobal(); 7786 } 7787 7788 return nullptr; 7789 } 7790 7791 JS_PUBLIC_API JSObject* JS::GetExecutionGlobalFromJSMicroTask( 7792 JS::JSMicroTask* entry) { 7793 JSObject* unwrapped = UncheckedUnwrap(entry); 7794 if (JS_IsDeadWrapper(unwrapped)) { 7795 return nullptr; 7796 } 7797 7798 if (unwrapped->is<PromiseReactionRecord>()) { 7799 // Use the stored equeue representative (which may need to be unwrapped) 7800 JSObject* enqueueGlobalRepresentative = 7801 unwrapped->as<PromiseReactionRecord>().enqueueGlobalRepresentative(); 7802 JSObject* unwrappedRepresentative = 7803 UncheckedUnwrap(enqueueGlobalRepresentative); 7804 7805 if (JS_IsDeadWrapper(unwrappedRepresentative)) { 7806 return nullptr; 7807 } 7808 7809 return &unwrappedRepresentative->nonCCWGlobal(); 7810 } 7811 7812 // Thenable jobs are allocated in the right realm+global and so we 7813 // can just use nonCCWGlobal; 7814 if (unwrapped->is<ThenableJob>()) { 7815 return &unwrapped->nonCCWGlobal(); 7816 } 7817 7818 MOZ_CRASH("Somehow we lost the execution global"); 7819 } 7820 7821 JS_PUBLIC_API JSObject* JS::MaybeGetPromiseFromJSMicroTask( 7822 JS::JSMicroTask* entry) { 7823 JSObject* unwrapped = UncheckedUnwrap(entry); 7824 if (JS_IsDeadWrapper(unwrapped)) { 7825 return nullptr; 7826 } 7827 7828 if (unwrapped->is<MicroTaskEntry>()) { 7829 return unwrapped->as<MicroTaskEntry>().promise(); 7830 } 7831 return nullptr; 7832 } 7833 7834 JS_PUBLIC_API bool JS::GetFlowIdFromJSMicroTask(JS::JSMicroTask* entry, 7835 uint64_t* uid) { 7836 // We want to make sure we get the flow id from the target object 7837 // not the wrapper. 7838 JSObject* unwrapped = UncheckedUnwrap(entry); 7839 if (JS_IsDeadWrapper(unwrapped)) { 7840 return false; 7841 } 7842 7843 MOZ_ASSERT(unwrapped->is<MicroTaskEntry>(), "Only use on JSMicroTasks"); 7844 7845 *uid = js::gc::GetUniqueIdInfallible(unwrapped); 7846 return true; 7847 } 7848 7849 JS_PUBLIC_API JS::JSMicroTask* JS::ToUnwrappedJSMicroTask( 7850 const JS::GenericMicroTask& genericMicroTask) { 7851 if (!genericMicroTask.isObject()) { 7852 return nullptr; 7853 } 7854 7855 JSObject* unwrapped = UncheckedUnwrap(&genericMicroTask.toObject()); 7856 7857 // On the off chance someone hands us a dead wrapper. 7858 if (JS_IsDeadWrapper(unwrapped)) { 7859 return nullptr; 7860 } 7861 if (!unwrapped->is<MicroTaskEntry>()) { 7862 return nullptr; 7863 } 7864 7865 return unwrapped; 7866 } 7867 7868 JS_PUBLIC_API JS::JSMicroTask* JS::ToMaybeWrappedJSMicroTask( 7869 const JS::GenericMicroTask& genericMicroTask) { 7870 if (!genericMicroTask.isObject()) { 7871 return nullptr; 7872 } 7873 7874 return &genericMicroTask.toObject(); 7875 } 7876 7877 JS_PUBLIC_API bool JS::IsJSMicroTask(const JS::GenericMicroTask& hv) { 7878 return JS::ToUnwrappedJSMicroTask(hv) != nullptr; 7879 } 7880 7881 JS::AutoDebuggerJobQueueInterruption::AutoDebuggerJobQueueInterruption() 7882 : cx(nullptr) {} 7883 7884 JS::AutoDebuggerJobQueueInterruption::~AutoDebuggerJobQueueInterruption() { 7885 MOZ_ASSERT_IF(initialized() && !cx->jobQueue->isDrainingStopped(), 7886 cx->jobQueue->empty()); 7887 } 7888 7889 bool JS::AutoDebuggerJobQueueInterruption::init(JSContext* cx) { 7890 MOZ_ASSERT(cx->jobQueue); 7891 this->cx = cx; 7892 saved = cx->jobQueue->saveJobQueue(cx); 7893 return !!saved; 7894 } 7895 7896 void JS::AutoDebuggerJobQueueInterruption::runJobs() { 7897 JS::AutoSaveExceptionState ases(cx); 7898 cx->jobQueue->runJobs(cx); 7899 } 7900 7901 const JSJitInfo promise_then_info = { 7902 {(JSJitGetterOp)Promise_then_noRetVal}, 7903 {0}, /* unused */ 7904 {0}, /* unused */ 7905 JSJitInfo::IgnoresReturnValueNative, 7906 JSJitInfo::AliasEverything, 7907 JSVAL_TYPE_UNDEFINED, 7908 }; 7909 7910 const JSJitInfo promise_catch_info = { 7911 {(JSJitGetterOp)Promise_catch_noRetVal}, 7912 {0}, /* unused */ 7913 {0}, /* unused */ 7914 JSJitInfo::IgnoresReturnValueNative, 7915 JSJitInfo::AliasEverything, 7916 JSVAL_TYPE_UNDEFINED, 7917 }; 7918 7919 static const JSFunctionSpec promise_methods[] = { 7920 JS_FNINFO("then", js::Promise_then, &promise_then_info, 2, 0), 7921 JS_FNINFO("catch", Promise_catch, &promise_catch_info, 1, 0), 7922 JS_SELF_HOSTED_FN("finally", "Promise_finally", 1, 0), 7923 JS_FS_END, 7924 }; 7925 7926 static const JSPropertySpec promise_properties[] = { 7927 JS_STRING_SYM_PS(toStringTag, "Promise", JSPROP_READONLY), 7928 JS_PS_END, 7929 }; 7930 7931 static const JSFunctionSpec promise_static_methods[] = { 7932 JS_FN("all", Promise_static_all, 1, 0), 7933 JS_FN("allSettled", Promise_static_allSettled, 1, 0), 7934 #ifdef NIGHTLY_BUILD 7935 JS_FN("allKeyed", Promise_static_allKeyed, 1, 0), 7936 JS_FN("allSettledKeyed", Promise_static_allSettledKeyed, 1, 0), 7937 #endif 7938 JS_FN("any", Promise_static_any, 1, 0), 7939 JS_FN("race", Promise_static_race, 1, 0), 7940 JS_FN("reject", Promise_reject, 1, 0), 7941 JS_FN("resolve", js::Promise_static_resolve, 1, 0), 7942 JS_FN("withResolvers", Promise_static_withResolvers, 0, 0), 7943 JS_FN("try", Promise_static_try, 1, 0), 7944 JS_FS_END, 7945 }; 7946 7947 static const JSPropertySpec promise_static_properties[] = { 7948 JS_SYM_GET(species, js::Promise_static_species, 0), 7949 JS_PS_END, 7950 }; 7951 7952 static const ClassSpec PromiseObjectClassSpec = { 7953 GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>, 7954 GenericCreatePrototype<PromiseObject>, 7955 promise_static_methods, 7956 promise_static_properties, 7957 promise_methods, 7958 promise_properties, 7959 GenericFinishInit<WhichHasRealmFuseProperty::ProtoAndCtor>, 7960 }; 7961 7962 const JSClass PromiseObject::class_ = { 7963 "Promise", 7964 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | 7965 JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) | 7966 JSCLASS_HAS_XRAYED_CONSTRUCTOR, 7967 JS_NULL_CLASS_OPS, 7968 &PromiseObjectClassSpec, 7969 }; 7970 7971 const JSClass PromiseObject::protoClass_ = { 7972 "Promise.prototype", 7973 JSCLASS_HAS_CACHED_PROTO(JSProto_Promise), 7974 JS_NULL_CLASS_OPS, 7975 &PromiseObjectClassSpec, 7976 };