Source code

Revision control

Other Tools

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/Debug.h"
18
#include "js/ForOfIterator.h" // JS::ForOfIterator
19
#include "js/PropertySpec.h"
20
#include "vm/ArrayObject.h"
21
#include "vm/AsyncFunction.h"
22
#include "vm/AsyncIteration.h"
23
#include "vm/ErrorObject.h"
24
#include "vm/GeneratorObject.h"
25
#include "vm/Iteration.h"
26
#include "vm/JSContext.h"
27
#include "vm/JSObject.h"
28
#include "vm/PromiseLookup.h" // js::PromiseLookup
29
#include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
30
#include "vm/SelfHosting.h"
31
32
#include "debugger/DebugAPI-inl.h"
33
#include "vm/Compartment-inl.h"
34
#include "vm/ErrorObject-inl.h"
35
#include "vm/JSContext-inl.h" // JSContext::check
36
#include "vm/JSObject-inl.h"
37
#include "vm/NativeObject-inl.h"
38
39
using namespace js;
40
41
static double MillisecondsSinceStartup(
42
const mozilla::Maybe<mozilla::TimeStamp>& maybeNow) {
43
auto now = maybeNow.isSome() ? maybeNow.ref() : mozilla::TimeStamp::Now();
44
return (now - mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
45
}
46
47
enum PromiseHandler {
48
PromiseHandlerIdentity = 0,
49
PromiseHandlerThrower,
50
51
// ES 2018 draft 25.5.5.4-5.
52
PromiseHandlerAsyncFunctionAwaitedFulfilled,
53
PromiseHandlerAsyncFunctionAwaitedRejected,
54
55
// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
56
// 6.2.3.1.1 Await Fulfilled Functions
57
// 6.2.3.1.2 Await Rejected Functions
58
PromiseHandlerAsyncGeneratorAwaitedFulfilled,
59
PromiseHandlerAsyncGeneratorAwaitedRejected,
60
61
// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
62
// 25.5.3.5.1 AsyncGeneratorResumeNext Return Processor Fulfilled Functions
63
// 25.5.3.5.2 AsyncGeneratorResumeNext Return Processor Rejected Functions
64
PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled,
65
PromiseHandlerAsyncGeneratorResumeNextReturnRejected,
66
67
// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
68
// 25.5.3.7 AsyncGeneratorYield, steps 8.c-e.
69
PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled,
70
PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected,
71
72
// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c
73
// 25.1.4.2.5 Async-from-Sync Iterator Value Unwrap Functions
74
//
75
// Async-from-Sync iterator handlers take the resolved value and create new
76
// iterator objects. To do so it needs to forward whether the iterator is
77
// done. In spec, this is achieved via the [[Done]] internal slot. We
78
// enumerate both true and false cases here.
79
PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone,
80
PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone,
81
82
// One past the maximum allowed PromiseHandler value.
83
PromiseHandlerLimit
84
};
85
86
enum ResolutionMode { ResolveMode, RejectMode };
87
88
enum ResolveFunctionSlots {
89
ResolveFunctionSlot_Promise = 0,
90
ResolveFunctionSlot_RejectFunction,
91
};
92
93
enum RejectFunctionSlots {
94
RejectFunctionSlot_Promise = 0,
95
RejectFunctionSlot_ResolveFunction,
96
};
97
98
enum PromiseCombinatorElementFunctionSlots {
99
PromiseCombinatorElementFunctionSlot_Data = 0,
100
PromiseCombinatorElementFunctionSlot_ElementIndex,
101
};
102
103
enum ReactionJobSlots {
104
ReactionJobSlot_ReactionRecord = 0,
105
};
106
107
enum ThenableJobSlots {
108
// The handler to use as the Promise reaction. It is a callable object
109
// that's guaranteed to be from the same compartment as the
110
// PromiseReactionJob.
111
ThenableJobSlot_Handler = 0,
112
113
// JobData - a, potentially CCW-wrapped, dense list containing data
114
// required for proper execution of the reaction.
115
ThenableJobSlot_JobData,
116
};
117
118
enum ThenableJobDataIndices {
119
// The Promise to resolve using the given thenable.
120
ThenableJobDataIndex_Promise = 0,
121
122
// The thenable to use as the receiver when calling the `then` function.
123
ThenableJobDataIndex_Thenable,
124
125
ThenableJobDataLength,
126
};
127
128
enum BuiltinThenableJobSlots {
129
// The Promise to resolve using the given thenable.
130
BuiltinThenableJobSlot_Promise = 0,
131
132
// The thenable to use as the receiver when calling the built-in `then`
133
// function.
134
BuiltinThenableJobSlot_Thenable,
135
};
136
137
struct PromiseCapability {
138
JSObject* promise = nullptr;
139
JSObject* resolve = nullptr;
140
JSObject* reject = nullptr;
141
142
PromiseCapability() = default;
143
144
void trace(JSTracer* trc);
145
};
146
147
void PromiseCapability::trace(JSTracer* trc) {
148
if (promise) {
149
TraceRoot(trc, &promise, "PromiseCapability::promise");
150
}
151
if (resolve) {
152
TraceRoot(trc, &resolve, "PromiseCapability::resolve");
153
}
154
if (reject) {
155
TraceRoot(trc, &reject, "PromiseCapability::reject");
156
}
157
}
158
159
namespace js {
160
161
template <typename Wrapper>
162
class WrappedPtrOperations<PromiseCapability, Wrapper> {
163
const PromiseCapability& capability() const {
164
return static_cast<const Wrapper*>(this)->get();
165
}
166
167
public:
168
HandleObject promise() const {
169
return HandleObject::fromMarkedLocation(&capability().promise);
170
}
171
HandleObject resolve() const {
172
return HandleObject::fromMarkedLocation(&capability().resolve);
173
}
174
HandleObject reject() const {
175
return HandleObject::fromMarkedLocation(&capability().reject);
176
}
177
};
178
179
template <typename Wrapper>
180
class MutableWrappedPtrOperations<PromiseCapability, Wrapper>
181
: public WrappedPtrOperations<PromiseCapability, Wrapper> {
182
PromiseCapability& capability() { return static_cast<Wrapper*>(this)->get(); }
183
184
public:
185
MutableHandleObject promise() {
186
return MutableHandleObject::fromMarkedLocation(&capability().promise);
187
}
188
MutableHandleObject resolve() {
189
return MutableHandleObject::fromMarkedLocation(&capability().resolve);
190
}
191
MutableHandleObject reject() {
192
return MutableHandleObject::fromMarkedLocation(&capability().reject);
193
}
194
};
195
196
} // namespace js
197
198
struct PromiseCombinatorElements;
199
200
class PromiseCombinatorDataHolder : public NativeObject {
201
enum {
202
Slot_Promise = 0,
203
Slot_RemainingElements,
204
Slot_ValuesArray,
205
Slot_ResolveOrRejectFunction,
206
SlotsCount,
207
};
208
209
public:
210
static const JSClass class_;
211
JSObject* promiseObj() { return &getFixedSlot(Slot_Promise).toObject(); }
212
JSObject* resolveOrRejectObj() {
213
return &getFixedSlot(Slot_ResolveOrRejectFunction).toObject();
214
}
215
Value valuesArray() { return getFixedSlot(Slot_ValuesArray); }
216
int32_t remainingCount() {
217
return getFixedSlot(Slot_RemainingElements).toInt32();
218
}
219
int32_t increaseRemainingCount() {
220
int32_t remainingCount = getFixedSlot(Slot_RemainingElements).toInt32();
221
remainingCount++;
222
setFixedSlot(Slot_RemainingElements, Int32Value(remainingCount));
223
return remainingCount;
224
}
225
int32_t decreaseRemainingCount() {
226
int32_t remainingCount = getFixedSlot(Slot_RemainingElements).toInt32();
227
remainingCount--;
228
MOZ_ASSERT(remainingCount >= 0, "unpaired calls to decreaseRemainingCount");
229
setFixedSlot(Slot_RemainingElements, Int32Value(remainingCount));
230
return remainingCount;
231
}
232
233
static PromiseCombinatorDataHolder* New(
234
JSContext* cx, HandleObject resultPromise,
235
Handle<PromiseCombinatorElements> elements, HandleObject resolveOrReject);
236
};
237
238
const JSClass PromiseCombinatorDataHolder::class_ = {
239
"PromiseCombinatorDataHolder", JSCLASS_HAS_RESERVED_SLOTS(SlotsCount)};
240
241
// Smart pointer to the "F.[[Values]]" part of the state of a Promise.all or
242
// Promise.allSettled invocation, or the "F.[[Errors]]" part of the state of a
243
// Promise.any invocation. Copes with compartment issues when setting an
244
// element.
245
struct MOZ_STACK_CLASS PromiseCombinatorElements final {
246
// Object value holding the elements array. The object can be a wrapper.
247
Value value;
248
249
// Unwrapped elements array. May not belong to the current compartment!
250
ArrayObject* unwrappedArray = nullptr;
251
252
// Set to true if the |setElement| method needs to wrap its input value.
253
bool setElementNeedsWrapping = false;
254
255
PromiseCombinatorElements() = default;
256
257
void trace(JSTracer* trc);
258
};
259
260
void PromiseCombinatorElements::trace(JSTracer* trc) {
261
TraceRoot(trc, &value, "PromiseCombinatorElements::value");
262
if (unwrappedArray) {
263
TraceRoot(trc, &unwrappedArray,
264
"PromiseCombinatorElements::unwrappedArray");
265
}
266
}
267
268
namespace js {
269
270
template <typename Wrapper>
271
class WrappedPtrOperations<PromiseCombinatorElements, Wrapper> {
272
const PromiseCombinatorElements& elements() const {
273
return static_cast<const Wrapper*>(this)->get();
274
}
275
276
public:
277
HandleValue value() const {
278
return HandleValue::fromMarkedLocation(&elements().value);
279
}
280
281
HandleArrayObject unwrappedArray() const {
282
return HandleArrayObject::fromMarkedLocation(&elements().unwrappedArray);
283
}
284
};
285
286
template <typename Wrapper>
287
class MutableWrappedPtrOperations<PromiseCombinatorElements, Wrapper>
288
: public WrappedPtrOperations<PromiseCombinatorElements, Wrapper> {
289
PromiseCombinatorElements& elements() {
290
return static_cast<Wrapper*>(this)->get();
291
}
292
293
public:
294
MutableHandleValue value() {
295
return MutableHandleValue::fromMarkedLocation(&elements().value);
296
}
297
298
MutableHandle<ArrayObject*> unwrappedArray() {
299
return MutableHandle<ArrayObject*>::fromMarkedLocation(
300
&elements().unwrappedArray);
301
}
302
303
void initialize(ArrayObject* arrayObj) {
304
unwrappedArray().set(arrayObj);
305
value().setObject(*arrayObj);
306
307
// |needsWrapping| isn't tracked here, because all modifications on the
308
// initial elements don't require any wrapping.
309
}
310
311
void initialize(PromiseCombinatorDataHolder* data, ArrayObject* arrayObj,
312
bool needsWrapping) {
313
unwrappedArray().set(arrayObj);
314
value().set(data->valuesArray());
315
elements().setElementNeedsWrapping = needsWrapping;
316
}
317
318
MOZ_MUST_USE bool pushUndefined(JSContext* cx) {
319
// Helper for the AutoRealm we need to work with |array|. We mostly do this
320
// for performance; we could go ahead and do the define via a cross-
321
// compartment proxy instead...
322
AutoRealm ar(cx, unwrappedArray());
323
324
HandleArrayObject arrayObj = unwrappedArray();
325
return js::NewbornArrayPush(cx, arrayObj, UndefinedValue());
326
}
327
328
// `Promise.all` Resolve Element Functions
329
// Step 9. Set values[index] to x.
330
//
331
// `Promise.allSettled` Resolve Element Functions
332
// `Promise.allSettled` Reject Element Functions
333
// Step 12. Set values[index] to obj.
334
//
335
// `Promise.any` Reject Element Functions
336
// Step 9. Set errors[index] to x.
337
//
338
// These handler functions are always created in the compartment of the
339
// Promise.all/allSettled/any function, which isn't necessarily the same
340
// compartment as unwrappedArray as explained in NewPromiseCombinatorElements.
341
// So before storing |val| we may need to enter unwrappedArray's compartment.
342
MOZ_MUST_USE bool setElement(JSContext* cx, uint32_t index, HandleValue val) {
343
// The index is guaranteed to be initialized to `undefined`.
344
MOZ_ASSERT(unwrappedArray()->getDenseElement(index).isUndefined());
345
346
if (elements().setElementNeedsWrapping) {
347
AutoRealm ar(cx, unwrappedArray());
348
349
RootedValue rootedVal(cx, val);
350
if (!cx->compartment()->wrap(cx, &rootedVal)) {
351
return false;
352
}
353
unwrappedArray()->setDenseElement(index, rootedVal);
354
} else {
355
unwrappedArray()->setDenseElement(index, val);
356
}
357
return true;
358
}
359
};
360
361
} // namespace js
362
363
PromiseCombinatorDataHolder* PromiseCombinatorDataHolder::New(
364
JSContext* cx, HandleObject resultPromise,
365
Handle<PromiseCombinatorElements> elements, HandleObject resolveOrReject) {
366
auto* dataHolder = NewBuiltinClassInstance<PromiseCombinatorDataHolder>(cx);
367
if (!dataHolder) {
368
return nullptr;
369
}
370
371
cx->check(resultPromise);
372
cx->check(elements.value());
373
cx->check(resolveOrReject);
374
375
dataHolder->setFixedSlot(Slot_Promise, ObjectValue(*resultPromise));
376
dataHolder->setFixedSlot(Slot_RemainingElements, Int32Value(1));
377
dataHolder->setFixedSlot(Slot_ValuesArray, elements.value());
378
dataHolder->setFixedSlot(Slot_ResolveOrRejectFunction,
379
ObjectValue(*resolveOrReject));
380
return dataHolder;
381
}
382
383
namespace {
384
// Generator used by PromiseObject::getID.
385
mozilla::Atomic<uint64_t> gIDGenerator(0);
386
} // namespace
387
388
static MOZ_ALWAYS_INLINE bool ShouldCaptureDebugInfo(JSContext* cx) {
389
return cx->options().asyncStack() || cx->realm()->isDebuggee();
390
}
391
392
static mozilla::Maybe<mozilla::TimeStamp> MaybeNow() {
393
// ShouldCaptureDebugInfo() may return inconsistent values when recording
394
// or replaying, so in places where we might need the current time for
395
// promise debug info we always capture the current time.
396
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
397
return mozilla::Some(mozilla::TimeStamp::Now());
398
}
399
return mozilla::Nothing();
400
}
401
402
class PromiseDebugInfo : public NativeObject {
403
private:
404
enum Slots {
405
Slot_AllocationSite,
406
Slot_ResolutionSite,
407
Slot_AllocationTime,
408
Slot_ResolutionTime,
409
Slot_Id,
410
SlotCount
411
};
412
413
public:
414
static const JSClass class_;
415
static PromiseDebugInfo* create(
416
JSContext* cx, Handle<PromiseObject*> promise,
417
const mozilla::Maybe<mozilla::TimeStamp>& maybeNow) {
418
Rooted<PromiseDebugInfo*> debugInfo(
419
cx, NewBuiltinClassInstance<PromiseDebugInfo>(cx));
420
if (!debugInfo) {
421
return nullptr;
422
}
423
424
RootedObject stack(cx);
425
if (!JS::CaptureCurrentStack(cx, &stack,
426
JS::StackCapture(JS::AllFrames()))) {
427
return nullptr;
428
}
429
debugInfo->setFixedSlot(Slot_AllocationSite, ObjectOrNullValue(stack));
430
debugInfo->setFixedSlot(Slot_ResolutionSite, NullValue());
431
debugInfo->setFixedSlot(Slot_AllocationTime,
432
DoubleValue(MillisecondsSinceStartup(maybeNow)));
433
debugInfo->setFixedSlot(Slot_ResolutionTime, NumberValue(0));
434
promise->setFixedSlot(PromiseSlot_DebugInfo, ObjectValue(*debugInfo));
435
436
return debugInfo;
437
}
438
439
static PromiseDebugInfo* FromPromise(PromiseObject* promise) {
440
Value val = promise->getFixedSlot(PromiseSlot_DebugInfo);
441
if (val.isObject()) {
442
return &val.toObject().as<PromiseDebugInfo>();
443
}
444
return nullptr;
445
}
446
447
/**
448
* Returns the given PromiseObject's process-unique ID.
449
* The ID is lazily assigned when first queried, and then either stored
450
* in the DebugInfo slot if no debug info was recorded for this Promise,
451
* or in the Id slot of the DebugInfo object.
452
*/
453
static uint64_t id(PromiseObject* promise) {
454
Value idVal(promise->getFixedSlot(PromiseSlot_DebugInfo));
455
if (idVal.isUndefined()) {
456
idVal.setDouble(++gIDGenerator);
457
promise->setFixedSlot(PromiseSlot_DebugInfo, idVal);
458
} else if (idVal.isObject()) {
459
PromiseDebugInfo* debugInfo = FromPromise(promise);
460
idVal = debugInfo->getFixedSlot(Slot_Id);
461
if (idVal.isUndefined()) {
462
idVal.setDouble(++gIDGenerator);
463
debugInfo->setFixedSlot(Slot_Id, idVal);
464
}
465
}
466
return uint64_t(idVal.toNumber());
467
}
468
469
double allocationTime() {
470
return getFixedSlot(Slot_AllocationTime).toNumber();
471
}
472
double resolutionTime() {
473
return getFixedSlot(Slot_ResolutionTime).toNumber();
474
}
475
JSObject* allocationSite() {
476
return getFixedSlot(Slot_AllocationSite).toObjectOrNull();
477
}
478
JSObject* resolutionSite() {
479
return getFixedSlot(Slot_ResolutionSite).toObjectOrNull();
480
}
481
482
static void setResolutionInfo(JSContext* cx, Handle<PromiseObject*> promise) {
483
mozilla::Maybe<mozilla::TimeStamp> maybeNow = MaybeNow();
484
485
if (!ShouldCaptureDebugInfo(cx)) {
486
return;
487
}
488
mozilla::recordreplay::AutoDisallowThreadEvents disallow;
489
490
// If async stacks weren't enabled and the Promise's global wasn't a
491
// debuggee when the Promise was created, we won't have a debugInfo
492
// object. We still want to capture the resolution stack, so we
493
// create the object now and change it's slots' values around a bit.
494
Rooted<PromiseDebugInfo*> debugInfo(cx, FromPromise(promise));
495
if (!debugInfo) {
496
RootedValue idVal(cx, promise->getFixedSlot(PromiseSlot_DebugInfo));
497
debugInfo = create(cx, promise, maybeNow);
498
if (!debugInfo) {
499
cx->clearPendingException();
500
return;
501
}
502
503
// The current stack was stored in the AllocationSite slot, move
504
// it to ResolutionSite as that's what it really is.
505
debugInfo->setFixedSlot(Slot_ResolutionSite,
506
debugInfo->getFixedSlot(Slot_AllocationSite));
507
debugInfo->setFixedSlot(Slot_AllocationSite, NullValue());
508
509
// There's no good default for a missing AllocationTime, so
510
// instead of resetting that, ensure that it's the same as
511
// ResolutionTime, so that the diff shows as 0, which isn't great,
512
// but bearable.
513
debugInfo->setFixedSlot(Slot_ResolutionTime,
514
debugInfo->getFixedSlot(Slot_AllocationTime));
515
516
// The Promise's ID might've been queried earlier, in which case
517
// it's stored in the DebugInfo slot. We saved that earlier, so
518
// now we can store it in the right place (or leave it as
519
// undefined if it wasn't ever initialized.)
520
debugInfo->setFixedSlot(Slot_Id, idVal);
521
return;
522
}
523
524
RootedObject stack(cx);
525
if (!JS::CaptureCurrentStack(cx, &stack,
526
JS::StackCapture(JS::AllFrames()))) {
527
cx->clearPendingException();
528
return;
529
}
530
531
debugInfo->setFixedSlot(Slot_ResolutionSite, ObjectOrNullValue(stack));
532
debugInfo->setFixedSlot(Slot_ResolutionTime,
533
DoubleValue(MillisecondsSinceStartup(maybeNow)));
534
}
535
};
536
537
const JSClass PromiseDebugInfo::class_ = {
538
"PromiseDebugInfo", JSCLASS_HAS_RESERVED_SLOTS(SlotCount)};
539
540
double PromiseObject::allocationTime() {
541
auto debugInfo = PromiseDebugInfo::FromPromise(this);
542
if (debugInfo) {
543
return debugInfo->allocationTime();
544
}
545
return 0;
546
}
547
548
double PromiseObject::resolutionTime() {
549
auto debugInfo = PromiseDebugInfo::FromPromise(this);
550
if (debugInfo) {
551
return debugInfo->resolutionTime();
552
}
553
return 0;
554
}
555
556
JSObject* PromiseObject::allocationSite() {
557
auto debugInfo = PromiseDebugInfo::FromPromise(this);
558
if (debugInfo) {
559
return debugInfo->allocationSite();
560
}
561
return nullptr;
562
}
563
564
JSObject* PromiseObject::resolutionSite() {
565
auto debugInfo = PromiseDebugInfo::FromPromise(this);
566
if (debugInfo) {
567
return debugInfo->resolutionSite();
568
}
569
return nullptr;
570
}
571
572
/**
573
* Wrapper for GetAndClearException that handles cases where no exception is
574
* pending, but an error occurred. This can be the case if an OOM was
575
* encountered while throwing the error.
576
*/
577
static bool MaybeGetAndClearException(JSContext* cx, MutableHandleValue rval) {
578
if (!cx->isExceptionPending()) {
579
return false;
580
}
581
582
return GetAndClearException(cx, rval);
583
}
584
585
static MOZ_MUST_USE bool RunRejectFunction(JSContext* cx,
586
HandleObject onRejectedFunc,
587
HandleValue result,
588
HandleObject promiseObj,
589
UnhandledRejectionBehavior behavior);
590
591
// ES2016, 25.4.1.1.1, Steps 1.a-b.
592
// Extracting all of this internal spec algorithm into a helper function would
593
// be tedious, so the check in step 1 and the entirety of step 2 aren't
594
// included.
595
static bool AbruptRejectPromise(JSContext* cx, CallArgs& args,
596
HandleObject promiseObj, HandleObject reject) {
597
// Step 1.a.
598
RootedValue reason(cx);
599
if (!MaybeGetAndClearException(cx, &reason)) {
600
return false;
601
}
602
603
if (!RunRejectFunction(cx, reject, reason, promiseObj,
604
UnhandledRejectionBehavior::Report)) {
605
return false;
606
}
607
608
// Step 1.b.
609
args.rval().setObject(*promiseObj);
610
return true;
611
}
612
613
static bool AbruptRejectPromise(JSContext* cx, CallArgs& args,
614
Handle<PromiseCapability> capability) {
615
return AbruptRejectPromise(cx, args, capability.promise(),
616
capability.reject());
617
}
618
619
enum ReactionRecordSlots {
620
// This is the promise-like object that gets resolved with the result of this
621
// reaction, if any. If this reaction record was created with .then or .catch,
622
// this is the promise that .then or .catch returned.
623
//
624
// The spec says that a PromiseReaction record has a [[Capability]] field
625
// whose value is either undefined or a PromiseCapability record, but we just
626
// store the PromiseCapability's fields directly in this object. This is the
627
// capability's [[Promise]] field; its [[Resolve]] and [[Reject]] fields are
628
// stored in ReactionRecordSlot_Resolve and ReactionRecordSlot_Reject.
629
//
630
// This can be 'null' in reaction records created for a few situations:
631
//
632
// - When you resolve one promise to another. When you pass a promise P1 to
633
// the 'fulfill' function of a promise P2, so that resolving P1 resolves P2
634
// in the same way, P1 gets a reaction record with the
635
// REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag set and whose
636
// ReactionRecordSlot_GeneratorOrPromiseToResolve slot holds P2.
637
//
638
// - When you await a promise. When an async function or generator awaits a
639
// value V, then the await expression generates an internal promise P,
640
// resolves it to V, and then gives P a reaction record with the
641
// REACTION_FLAG_ASYNC_FUNCTION or REACTION_FLAG_ASYNC_GENERATOR flag set
642
// and whose ReactionRecordSlot_GeneratorOrPromiseToResolve slot holds the
643
// generator object. (Typically V is a promise, so resolving P to V gives V
644
// a REACTION_FLAGS_DEFAULT_RESOLVING_HANDLER reaction record as described
645
// above.)
646
//
647
// - When JS::AddPromiseReactions{,IgnoringUnhandledRejection} cause the
648
// reaction to be created. (These functions act as if they had created a
649
// promise to invoke the appropriate provided reaction function, without
650
// actually allocating a promise for them.)
651
ReactionRecordSlot_Promise = 0,
652
653
// The [[Handler]] field(s) of a PromiseReaction record. We create a
654
// single reaction record for fulfillment and rejection, therefore our
655
// PromiseReaction implementation needs two [[Handler]] fields.
656
//
657
// The slot value is either a callable object, an integer constant from
658
// the |PromiseHandler| enum, or null. If the value is null, either the
659
// REACTION_FLAG_DEBUGGER_DUMMY or the
660
// REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag must be set.
661
//
662
// After setting the target state for a PromiseReaction, the slot of the
663
// no longer used handler gets reused to store the argument of the active
664
// handler.
665
ReactionRecordSlot_OnFulfilled,
666
ReactionRecordSlot_OnRejectedArg = ReactionRecordSlot_OnFulfilled,
667
ReactionRecordSlot_OnRejected,
668
ReactionRecordSlot_OnFulfilledArg = ReactionRecordSlot_OnRejected,
669
670
// The functions to resolve or reject the promise. Matches the
671
// [[Capability]].[[Resolve]] and [[Capability]].[[Reject]] fields from
672
// the spec.
673
//
674
// The slot values are either callable objects or null, but the latter
675
// case is only allowed if the promise is either a built-in Promise object
676
// or null.
677
ReactionRecordSlot_Resolve,
678
ReactionRecordSlot_Reject,
679
680
// The incumbent global for this reaction record. Can be null.
681
ReactionRecordSlot_IncumbentGlobalObject,
682
683
// Bitmask of the REACTION_FLAG values.
684
ReactionRecordSlot_Flags,
685
686
// Additional slot to store extra data for specific reaction record types.
687
//
688
// - When the REACTION_FLAG_ASYNC_FUNCTION flag is set, this slot stores
689
// the (internal) generator object for this promise reaction.
690
// - When the REACTION_FLAG_ASYNC_GENERATOR flag is set, this slot stores
691
// the async generator object for this promise reaction.
692
// - When the REACTION_FLAG_DEFAULT_RESOLVING_HANDLER flag is set, this
693
// slot stores the promise to resolve when conceptually "calling" the
694
// OnFulfilled or OnRejected handlers.
695
ReactionRecordSlot_GeneratorOrPromiseToResolve,
696
697
ReactionRecordSlots,
698
};
699
700
// ES2016, 25.4.1.2.
701
class PromiseReactionRecord : public NativeObject {
702
static constexpr uint32_t REACTION_FLAG_RESOLVED = 0x1;
703
static constexpr uint32_t REACTION_FLAG_FULFILLED = 0x2;
704
static constexpr uint32_t REACTION_FLAG_DEFAULT_RESOLVING_HANDLER = 0x4;
705
static constexpr uint32_t REACTION_FLAG_ASYNC_FUNCTION = 0x8;
706
static constexpr uint32_t REACTION_FLAG_ASYNC_GENERATOR = 0x10;
707
static constexpr uint32_t REACTION_FLAG_DEBUGGER_DUMMY = 0x20;
708
static constexpr uint32_t REACTION_FLAG_IGNORE_UNHANDLED_REJECTION = 0x40;
709
710
void setFlagOnInitialState(uint32_t flag) {
711
int32_t flags = this->flags();
712
MOZ_ASSERT(flags == 0, "Can't modify with non-default flags");
713
flags |= flag;
714
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
715
}
716
717
uint32_t handlerSlot() {
718
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
719
return targetState() == JS::PromiseState::Fulfilled
720
? ReactionRecordSlot_OnFulfilled
721
: ReactionRecordSlot_OnRejected;
722
}
723
724
uint32_t handlerArgSlot() {
725
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
726
return targetState() == JS::PromiseState::Fulfilled
727
? ReactionRecordSlot_OnFulfilledArg
728
: ReactionRecordSlot_OnRejectedArg;
729
}
730
731
public:
732
static const JSClass class_;
733
734
JSObject* promise() {
735
return getFixedSlot(ReactionRecordSlot_Promise).toObjectOrNull();
736
}
737
int32_t flags() const {
738
return getFixedSlot(ReactionRecordSlot_Flags).toInt32();
739
}
740
JS::PromiseState targetState() {
741
int32_t flags = this->flags();
742
if (!(flags & REACTION_FLAG_RESOLVED)) {
743
return JS::PromiseState::Pending;
744
}
745
return flags & REACTION_FLAG_FULFILLED ? JS::PromiseState::Fulfilled
746
: JS::PromiseState::Rejected;
747
}
748
void setTargetStateAndHandlerArg(JS::PromiseState state, const Value& arg) {
749
MOZ_ASSERT(targetState() == JS::PromiseState::Pending);
750
MOZ_ASSERT(state != JS::PromiseState::Pending,
751
"Can't revert a reaction to pending.");
752
753
int32_t flags = this->flags();
754
flags |= REACTION_FLAG_RESOLVED;
755
if (state == JS::PromiseState::Fulfilled) {
756
flags |= REACTION_FLAG_FULFILLED;
757
}
758
759
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
760
setFixedSlot(handlerArgSlot(), arg);
761
}
762
763
void setShouldIgnoreUnhandledRejection() {
764
setFlagOnInitialState(REACTION_FLAG_IGNORE_UNHANDLED_REJECTION);
765
}
766
UnhandledRejectionBehavior unhandledRejectionBehavior() const {
767
int32_t flags = this->flags();
768
return (flags & REACTION_FLAG_IGNORE_UNHANDLED_REJECTION)
769
? UnhandledRejectionBehavior::Ignore
770
: UnhandledRejectionBehavior::Report;
771
}
772
773
void setIsDefaultResolvingHandler(PromiseObject* promiseToResolve) {
774
setFlagOnInitialState(REACTION_FLAG_DEFAULT_RESOLVING_HANDLER);
775
setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
776
ObjectValue(*promiseToResolve));
777
}
778
bool isDefaultResolvingHandler() {
779
int32_t flags = this->flags();
780
return flags & REACTION_FLAG_DEFAULT_RESOLVING_HANDLER;
781
}
782
PromiseObject* defaultResolvingPromise() {
783
MOZ_ASSERT(isDefaultResolvingHandler());
784
const Value& promiseToResolve =
785
getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
786
return &promiseToResolve.toObject().as<PromiseObject>();
787
}
788
void setIsAsyncFunction(AsyncFunctionGeneratorObject* genObj) {
789
setFlagOnInitialState(REACTION_FLAG_ASYNC_FUNCTION);
790
setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
791
ObjectValue(*genObj));
792
}
793
bool isAsyncFunction() {
794
int32_t flags = this->flags();
795
return flags & REACTION_FLAG_ASYNC_FUNCTION;
796
}
797
AsyncFunctionGeneratorObject* asyncFunctionGenerator() {
798
MOZ_ASSERT(isAsyncFunction());
799
const Value& generator =
800
getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
801
return &generator.toObject().as<AsyncFunctionGeneratorObject>();
802
}
803
void setIsAsyncGenerator(AsyncGeneratorObject* asyncGenObj) {
804
setFlagOnInitialState(REACTION_FLAG_ASYNC_GENERATOR);
805
setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
806
ObjectValue(*asyncGenObj));
807
}
808
bool isAsyncGenerator() {
809
int32_t flags = this->flags();
810
return flags & REACTION_FLAG_ASYNC_GENERATOR;
811
}
812
AsyncGeneratorObject* asyncGenerator() {
813
MOZ_ASSERT(isAsyncGenerator());
814
const Value& generator =
815
getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
816
return &generator.toObject().as<AsyncGeneratorObject>();
817
}
818
void setIsDebuggerDummy() {
819
setFlagOnInitialState(REACTION_FLAG_DEBUGGER_DUMMY);
820
}
821
bool isDebuggerDummy() {
822
int32_t flags = this->flags();
823
return flags & REACTION_FLAG_DEBUGGER_DUMMY;
824
}
825
Value handler() {
826
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
827
return getFixedSlot(handlerSlot());
828
}
829
Value handlerArg() {
830
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
831
return getFixedSlot(handlerArgSlot());
832
}
833
JSObject* getAndClearIncumbentGlobalObject() {
834
JSObject* obj =
835
getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject).toObjectOrNull();
836
setFixedSlot(ReactionRecordSlot_IncumbentGlobalObject, UndefinedValue());
837
return obj;
838
}
839
};
840
841
const JSClass PromiseReactionRecord::class_ = {
842
"PromiseReactionRecord", JSCLASS_HAS_RESERVED_SLOTS(ReactionRecordSlots)};
843
844
static void AddPromiseFlags(PromiseObject& promise, int32_t flag) {
845
int32_t flags = promise.flags();
846
promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags | flag));
847
}
848
849
static void RemovePromiseFlags(PromiseObject& promise, int32_t flag) {
850
int32_t flags = promise.flags();
851
promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags & ~flag));
852
}
853
854
static bool PromiseHasAnyFlag(PromiseObject& promise, int32_t flag) {
855
return promise.flags() & flag;
856
}
857
858
static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp);
859
static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp);
860
861
// ES2016, 25.4.1.3.
862
static MOZ_MUST_USE MOZ_ALWAYS_INLINE bool CreateResolvingFunctions(
863
JSContext* cx, HandleObject promise, MutableHandleObject resolveFn,
864
MutableHandleObject rejectFn) {
865
HandlePropertyName funName = cx->names().empty;
866
resolveFn.set(NewNativeFunction(cx, ResolvePromiseFunction, 1, funName,
867
gc::AllocKind::FUNCTION_EXTENDED,
868
GenericObject));
869
if (!resolveFn) {
870
return false;
871
}
872
873
rejectFn.set(NewNativeFunction(cx, RejectPromiseFunction, 1, funName,
874
gc::AllocKind::FUNCTION_EXTENDED,
875
GenericObject));
876
if (!rejectFn) {
877
return false;
878
}
879
880
JSFunction* resolveFun = &resolveFn->as<JSFunction>();
881
JSFunction* rejectFun = &rejectFn->as<JSFunction>();
882
883
resolveFun->initExtendedSlot(ResolveFunctionSlot_Promise,
884
ObjectValue(*promise));
885
resolveFun->initExtendedSlot(ResolveFunctionSlot_RejectFunction,
886
ObjectValue(*rejectFun));
887
888
rejectFun->initExtendedSlot(RejectFunctionSlot_Promise,
889
ObjectValue(*promise));
890
rejectFun->initExtendedSlot(RejectFunctionSlot_ResolveFunction,
891
ObjectValue(*resolveFun));
892
893
return true;
894
}
895
896
static void ClearResolutionFunctionSlots(JSFunction* resolutionFun);
897
898
static bool IsSettledMaybeWrappedPromise(JSObject* promise) {
899
if (IsProxy(promise)) {
900
promise = UncheckedUnwrap(promise);
901
902
// Caller needs to handle dead wrappers.
903
if (JS_IsDeadWrapper(promise)) {
904
return false;
905
}
906
}
907
908
return promise->as<PromiseObject>().state() != JS::PromiseState::Pending;
909
}
910
911
// ES2016, 25.4.1.7.
912
static MOZ_MUST_USE bool RejectMaybeWrappedPromise(JSContext* cx,
913
HandleObject promiseObj,
914
HandleValue reason);
915
916
// ES2016, 25.4.1.7.
917
static MOZ_MUST_USE bool RejectPromiseInternal(JSContext* cx,
918
Handle<PromiseObject*> promise,
919
HandleValue reason);
920
921
// ES2016, 25.4.1.3.1.
922
static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp) {
923
CallArgs args = CallArgsFromVp(argc, vp);
924
925
JSFunction* reject = &args.callee().as<JSFunction>();
926
HandleValue reasonVal = args.get(0);
927
928
// Steps 1-2.
929
const Value& promiseVal = reject->getExtendedSlot(RejectFunctionSlot_Promise);
930
931
// Steps 3-4.
932
// If the Promise isn't available anymore, it has been resolved and the
933
// reference to it removed to make it eligible for collection.
934
if (promiseVal.isUndefined()) {
935
args.rval().setUndefined();
936
return true;
937
}
938
939
// Store the promise value in |promise| before ClearResolutionFunctionSlots
940
// removes the reference.
941
RootedObject promise(cx, &promiseVal.toObject());
942
943
// Step 5.
944
// Here, we only remove the Promise reference from the resolution
945
// functions. Actually marking it as fulfilled/rejected happens later.
946
ClearResolutionFunctionSlots(reject);
947
948
// In some cases the Promise reference on the resolution function won't
949
// have been removed during resolution, so we need to check that here,
950
// too.
951
if (IsSettledMaybeWrappedPromise(promise)) {
952
args.rval().setUndefined();
953
return true;
954
}
955
956
// Step 6.
957
if (!RejectMaybeWrappedPromise(cx, promise, reasonVal)) {
958
return false;
959
}
960
args.rval().setUndefined();
961
return true;
962
}
963
964
static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext* cx,
965
HandleObject promiseObj,
966
HandleValue value_);
967
968
static MOZ_MUST_USE bool EnqueuePromiseResolveThenableJob(
969
JSContext* cx, HandleValue promiseToResolve, HandleValue thenable,
970
HandleValue thenVal);
971
972
static MOZ_MUST_USE bool EnqueuePromiseResolveThenableBuiltinJob(
973
JSContext* cx, HandleObject promiseToResolve, HandleObject thenable);
974
975
static bool Promise_then_impl(JSContext* cx, HandleValue promiseVal,
976
HandleValue onFulfilled, HandleValue onRejected,
977
MutableHandleValue rval, bool rvalUsed);
978
979
// ES2016, 25.4.1.3.2, steps 6-13.
980
static MOZ_MUST_USE bool ResolvePromiseInternal(JSContext* cx,
981
HandleObject promise,
982
HandleValue resolutionVal) {
983
cx->check(promise, resolutionVal);
984
MOZ_ASSERT(!IsSettledMaybeWrappedPromise(promise));
985
986
// Step 7 (reordered).
987
if (!resolutionVal.isObject()) {
988
return FulfillMaybeWrappedPromise(cx, promise, resolutionVal);
989
}
990
991
RootedObject resolution(cx, &resolutionVal.toObject());
992
993
// Step 6.
994
if (resolution == promise) {
995
// Step 6.a.
996
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
997
JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF);
998
RootedValue selfResolutionError(cx);
999
if (!MaybeGetAndClearException(cx, &selfResolutionError)) {
1000
return false;
1001
}
1002
1003
// Step 6.b.
1004
return RejectMaybeWrappedPromise(cx, promise, selfResolutionError);
1005
}
1006
1007
// Step 8.
1008
RootedValue thenVal(cx);
1009
bool status =
1010
GetProperty(cx, resolution, resolution, cx->names().then, &thenVal);
1011
1012
RootedValue error(cx);
1013
if (!status) {
1014
if (!MaybeGetAndClearException(cx, &error)) {
1015
return false;
1016
}
1017
}
1018
1019
// Testing functions allow to directly settle a promise without going
1020
// through the resolving functions. In that case the normal bookkeeping to
1021
// ensure only pending promises can be resolved doesn't apply and we need
1022
// to manually check for already settled promises. The exception is simply
1023
// dropped when this case happens.
1024
if (IsSettledMaybeWrappedPromise(promise)) {
1025
return true;
1026
}
1027
1028
// Step 9.
1029
if (!status) {
1030
return RejectMaybeWrappedPromise(cx, promise, error);
1031
}
1032
1033
// Step 10 (implicit).
1034
1035
// Step 11.
1036
if (!IsCallable(thenVal)) {
1037
return FulfillMaybeWrappedPromise(cx, promise, resolutionVal);
1038
}
1039
1040
// If the resolution object is a built-in Promise object and the
1041
// `then` property is the original Promise.prototype.then function
1042
// from the current realm, we skip storing/calling it.
1043
// Additionally we require that |promise| itself is also a built-in
1044
// Promise object, so the fast path doesn't need to cope with wrappers.
1045
bool isBuiltinThen = false;
1046
if (resolution->is<PromiseObject>() && promise->is<PromiseObject>() &&
1047
IsNativeFunction(thenVal, Promise_then) &&
1048
thenVal.toObject().as<JSFunction>().realm() == cx->realm()) {
1049
isBuiltinThen = true;
1050
}
1051
1052
// Step 12.
1053
if (!isBuiltinThen) {
1054
RootedValue promiseVal(cx, ObjectValue(*promise));
1055
if (!EnqueuePromiseResolveThenableJob(cx, promiseVal, resolutionVal,
1056
thenVal)) {
1057
return false;
1058
}
1059
} else {
1060
if (!EnqueuePromiseResolveThenableBuiltinJob(cx, promise, resolution)) {
1061
return false;
1062
}
1063
}
1064
1065
// Step 13.
1066
return true;
1067
}
1068
1069
// ES2016, 25.4.1.3.2.
1070
static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) {
1071
CallArgs args = CallArgsFromVp(argc, vp);
1072
1073
JSFunction* resolve = &args.callee().as<JSFunction>();
1074
HandleValue resolutionVal = args.get(0);
1075
1076
// Steps 3-4 (reordered).
1077
// We use the reference to the reject function as a signal for whether
1078
// the resolve or reject function was already called, at which point
1079
// the references on each of the functions are cleared.
1080
if (!resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction)
1081
.isObject()) {
1082
args.rval().setUndefined();
1083
return true;
1084
}
1085
1086
// Steps 1-2 (reordered).
1087
RootedObject promise(
1088
cx, &resolve->getExtendedSlot(ResolveFunctionSlot_Promise).toObject());
1089
1090
// Step 5.
1091
// Here, we only remove the Promise reference from the resolution
1092
// functions. Actually marking it as fulfilled/rejected happens later.
1093
ClearResolutionFunctionSlots(resolve);
1094
1095
// In some cases the Promise reference on the resolution function won't
1096
// have been removed during resolution, so we need to check that here,
1097
// too.
1098
if (IsSettledMaybeWrappedPromise(promise)) {
1099
args.rval().setUndefined();
1100
return true;
1101
}
1102
1103
// Steps 6-13.
1104
if (!ResolvePromiseInternal(cx, promise, resolutionVal)) {
1105
return false;
1106
}
1107
args.rval().setUndefined();
1108
return true;
1109
}
1110
1111
static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
1112
1113
/**
1114
* Tells the embedding to enqueue a Promise reaction job, based on
1115
* three parameters:
1116
* reactionObj - The reaction record.
1117
* handlerArg_ - The first and only argument to pass to the handler invoked by
1118
* the job. This will be stored on the reaction record.
1119
* targetState - The PromiseState this reaction job targets. This decides
1120
* whether the onFulfilled or onRejected handler is called.
1121
*/
1122
MOZ_MUST_USE static bool EnqueuePromiseReactionJob(
1123
JSContext* cx, HandleObject reactionObj, HandleValue handlerArg_,
1124
JS::PromiseState targetState) {
1125
MOZ_ASSERT(targetState == JS::PromiseState::Fulfilled ||
1126
targetState == JS::PromiseState::Rejected);
1127
1128
// The reaction might have been stored on a Promise from another
1129
// compartment, which means it would've been wrapped in a CCW.
1130
// To properly handle that case here, unwrap it and enter its
1131
// compartment, where the job creation should take place anyway.
1132
Rooted<PromiseReactionRecord*> reaction(cx);
1133
RootedValue handlerArg(cx, handlerArg_);
1134
mozilla::Maybe<AutoRealm> ar;
1135
if (!IsProxy(reactionObj)) {
1136
MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
1137
reaction = &reactionObj->as<PromiseReactionRecord>();
1138
if (cx->realm() != reaction->realm()) {
1139
// If the compartment has multiple realms, create the job in the
1140
// reaction's realm. This is consistent with the code in the else-branch
1141
// and avoids problems with running jobs against a dying global (Gecko
1142
// drops such jobs).
1143
ar.emplace(cx, reaction);
1144
}
1145
} else {
1146
JSObject* unwrappedReactionObj = UncheckedUnwrap(reactionObj);
1147
if (JS_IsDeadWrapper(unwrappedReactionObj)) {
1148
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1149
JSMSG_DEAD_OBJECT);
1150
return false;
1151
}
1152
reaction = &unwrappedReactionObj->as<PromiseReactionRecord>();
1153
MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>());
1154
ar.emplace(cx, reaction);
1155
if (!cx->compartment()->wrap(cx, &handlerArg)) {
1156
return false;
1157
}
1158
}
1159
1160
// Must not enqueue a reaction job more than once.
1161
MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending);
1162
1163
cx->check(handlerArg);
1164
reaction->setTargetStateAndHandlerArg(targetState, handlerArg);
1165
1166
RootedValue reactionVal(cx, ObjectValue(*reaction));
1167
RootedValue handler(cx, reaction->handler());
1168
1169
// If we have a handler callback, we enter that handler's compartment so
1170
// that the promise reaction job function is created in that compartment.
1171
// That guarantees that the embedding ends up with the right entry global.
1172
// This is relevant for some html APIs like fetch that derive information
1173
// from said global.
1174
mozilla::Maybe<AutoRealm> ar2;
1175
if (handler.isObject()) {
1176
// The unwrapping has to be unchecked because we specifically want to
1177
// be able to use handlers with wrappers that would only allow calls.
1178
// E.g., it's ok to have a handler from a chrome compartment in a
1179
// reaction to a content compartment's Promise instance.
1180
JSObject* handlerObj = UncheckedUnwrap(&handler.toObject());
1181
MOZ_ASSERT(handlerObj);
1182
ar2.emplace(cx, handlerObj);
1183
1184
// We need to wrap the reaction to store it on the job function.
1185
if (!cx->compartment()->wrap(cx, &reactionVal)) {
1186
return false;
1187
}
1188
}
1189
1190
// Create the JS function to call when the job is triggered.
1191
HandlePropertyName funName = cx->names().empty;
1192
RootedFunction job(
1193
cx, NewNativeFunction(cx, PromiseReactionJob, 0, funName,
1194
gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1195
if (!job) {
1196
return false;
1197
}
1198
1199
// Store the reaction on the reaction job.
1200
job->setExtendedSlot(ReactionJobSlot_ReactionRecord, reactionVal);
1201
1202
// When using JS::AddPromiseReactions{,IgnoringUnHandledRejection}, no actual
1203
// promise is created, so we might not have one here.
1204
// Additionally, we might have an object here that isn't an instance of
1205
// Promise. This can happen if content overrides the value of
1206
// Promise[@@species] (or invokes Promise#then on a Promise subclass
1207
// instance with a non-default @@species value on the constructor) with a
1208
// function that returns objects that're not Promise (subclass) instances.
1209
// In that case, we just pretend we didn't have an object in the first
1210
// place.
1211
// If after all this we do have an object, wrap it in case we entered the
1212
// handler's compartment above, because we should pass objects from a
1213
// single compartment to the enqueuePromiseJob callback.
1214
RootedObject promise(cx, reaction->promise());
1215
if (promise) {
1216
if (promise->is<PromiseObject>()) {
1217
if (!cx->compartment()->wrap(cx, &promise)) {
1218
return false;
1219
}
1220
} else if (IsWrapper(promise)) {
1221
// `promise` can be already-wrapped promise object at this point.
1222
JSObject* unwrappedPromise = UncheckedUnwrap(promise);
1223
if (unwrappedPromise->is<PromiseObject>()) {
1224
if (!cx->compartment()->wrap(cx, &promise)) {
1225
return false;
1226
}
1227
} else {
1228
promise = nullptr;
1229
}
1230
} else {
1231
promise = nullptr;
1232
}
1233
}
1234
1235
// Using objectFromIncumbentGlobal, we can derive the incumbent global by
1236
// unwrapping and then getting the global. This is very convoluted, but
1237
// much better than having to store the original global as a private value
1238
// because we couldn't wrap it to store it as a normal JS value.
1239
Rooted<GlobalObject*> global(cx);
1240
if (JSObject* objectFromIncumbentGlobal =
1241
reaction->getAndClearIncumbentGlobalObject()) {
1242
objectFromIncumbentGlobal = CheckedUnwrapStatic(objectFromIncumbentGlobal);
1243
MOZ_ASSERT(objectFromIncumbentGlobal);
1244
global = &objectFromIncumbentGlobal->nonCCWGlobal();
1245
}
1246
1247
// Note: the global we pass here might be from a different compartment
1248
// than job and promise. While it's somewhat unusual to pass objects
1249
// from multiple compartments, in this case we specifically need the
1250
// global to be unwrapped because wrapping and unwrapping aren't
1251
// necessarily symmetric for globals.
1252
return cx->runtime()->enqueuePromiseJob(cx, job, promise, global);
1253
}
1254
1255
static MOZ_MUST_USE bool TriggerPromiseReactions(JSContext* cx,
1256
HandleValue reactionsVal,
1257
JS::PromiseState state,
1258
HandleValue valueOrReason);
1259
1260
// ES2016, Commoned-out implementation of 25.4.1.4. and 25.4.1.7.
1261
static MOZ_MUST_USE bool ResolvePromise(JSContext* cx,
1262
Handle<PromiseObject*> promise,
1263
HandleValue valueOrReason,
1264
JS::PromiseState state) {
1265
// Step 1.
1266
MOZ_ASSERT(promise->state() == JS::PromiseState::Pending);
1267
MOZ_ASSERT(state == JS::PromiseState::Fulfilled ||
1268
state == JS::PromiseState::Rejected);
1269
1270
// Step 2.
1271
// We only have one list of reactions for both resolution types. So
1272
// instead of getting the right list of reactions, we determine the
1273
// resolution type to retrieve the right information from the
1274
// reaction records.
1275
RootedValue reactionsVal(cx, promise->reactions());
1276
1277
// Steps 3-5.
1278
// The same slot is used for the reactions list and the result, so setting
1279
// the result also removes the reactions list.
1280
promise->setFixedSlot(PromiseSlot_ReactionsOrResult, valueOrReason);
1281
1282
// Step 6.
1283
int32_t flags = promise->flags();
1284
flags |= PROMISE_FLAG_RESOLVED;
1285
if (state == JS::PromiseState::Fulfilled) {
1286
flags |= PROMISE_FLAG_FULFILLED;
1287
}
1288
promise->setFixedSlot(PromiseSlot_Flags, Int32Value(flags));
1289
1290
// Also null out the resolve/reject functions so they can be GC'd.
1291
promise->setFixedSlot(PromiseSlot_RejectFunction, UndefinedValue());
1292
1293
// Now that everything else is done, do the things the debugger needs.
1294
// Step 7 of RejectPromise implemented in onSettled.
1295
PromiseObject::onSettled(cx, promise);
1296
1297
// Step 7 of FulfillPromise.
1298
// Step 8 of RejectPromise.
1299
return TriggerPromiseReactions(cx, reactionsVal, state, valueOrReason);
1300
}
1301
1302
// ES2016, 25.4.1.7.
1303
static MOZ_MUST_USE bool RejectPromiseInternal(JSContext* cx,
1304
Handle<PromiseObject*> promise,
1305
HandleValue reason) {
1306
return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected);
1307
}
1308
1309
// ES2016, 25.4.1.4.
1310
static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext* cx,
1311
HandleObject promiseObj,
1312
HandleValue value_) {
1313
Rooted<PromiseObject*> promise(cx);
1314
RootedValue value(cx, value_);
1315
1316
mozilla::Maybe<AutoRealm> ar;
1317
if (!IsProxy(promiseObj)) {
1318
promise = &promiseObj->as<PromiseObject>();
1319
} else {
1320
JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj);
1321
if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
1322
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1323
JSMSG_DEAD_OBJECT);
1324
return false;
1325
}
1326
promise = &unwrappedPromiseObj->as<PromiseObject>();
1327
ar.emplace(cx, promise);
1328
if (!cx->compartment()->wrap(cx, &value)) {
1329
return false;
1330
}
1331
}
1332
1333
return ResolvePromise(cx, promise, value, JS::PromiseState::Fulfilled);
1334
}
1335
1336
static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp);
1337
static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp);
1338
static MOZ_MUST_USE PromiseObject* CreatePromiseObjectInternal(
1339
JSContext* cx, HandleObject proto = nullptr, bool protoIsWrapped = false,
1340
bool informDebugger = true);
1341
1342
enum GetCapabilitiesExecutorSlots {
1343
GetCapabilitiesExecutorSlots_Resolve,
1344
GetCapabilitiesExecutorSlots_Reject
1345
};
1346
1347
static MOZ_MUST_USE PromiseObject*
1348
CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx) {
1349
PromiseObject* promise = CreatePromiseObjectInternal(cx);
1350
if (!promise) {
1351
return nullptr;
1352
}
1353
1354
AddPromiseFlags(*promise, PROMISE_FLAG_DEFAULT_RESOLVING_FUNCTIONS);
1355
return promise;
1356
}
1357
1358
static MOZ_MUST_USE PromiseObject* CreatePromiseWithDefaultResolutionFunctions(
1359
JSContext* cx, MutableHandleObject resolve, MutableHandleObject reject) {
1360
// ES2016, 25.4.3.1., as if called with GetCapabilitiesExecutor as the
1361
// executor argument.
1362
1363
// Steps 1-2 (Not applicable).
1364
1365
// Steps 3-7.
1366
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
1367
if (!promise) {
1368
return nullptr;
1369
}
1370
1371
// Step 8.
1372
if (!CreateResolvingFunctions(cx, promise, resolve, reject)) {
1373
return nullptr;
1374
}
1375
1376
promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*reject));
1377
1378
// Steps 9-10 (Not applicable).
1379
1380
// Step 11.
1381
return promise;
1382
}
1383
1384
// ES2016, 25.4.1.5.
1385
static MOZ_MUST_USE bool NewPromiseCapability(
1386
JSContext* cx, HandleObject C, MutableHandle<PromiseCapability> capability,
1387
bool canOmitResolutionFunctions) {
1388
RootedValue cVal(cx, ObjectValue(*C));
1389
1390
// Steps 1-2.
1391
if (!IsConstructor(C)) {
1392
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_SEARCH_STACK, cVal,
1393
nullptr);
1394
return false;
1395
}
1396
1397
// If we'd call the original Promise constructor and know that the
1398
// resolve/reject functions won't ever escape to content, we can skip
1399
// creating and calling the executor function and instead return a Promise
1400
// marked as having default resolve/reject functions.
1401
//
1402
// This can't be used in Promise.all and Promise.race because we have to
1403
// pass the reject (and resolve, in the race case) function to thenables
1404
// in the list passed to all/race, which (potentially) means exposing them
1405
// to content.
1406
//
1407
// For Promise.all and Promise.race we can only optimize away the creation
1408
// of the GetCapabilitiesExecutor function, and directly allocate the
1409
// result promise instead of invoking the Promise constructor.
1410
if (IsNativeFunction(cVal, PromiseConstructor) &&
1411
cVal.toObject().nonCCWRealm() == cx->realm()) {
1412
PromiseObject* promise;
1413
if (canOmitResolutionFunctions) {
1414
promise = CreatePromiseObjectWithoutResolutionFunctions(cx);
1415
} else {
1416
promise = CreatePromiseWithDefaultResolutionFunctions(
1417
cx, capability.resolve(), capability.reject());
1418
}
1419
if (!promise) {
1420
return false;
1421
}
1422
1423
capability.promise().set(promise);
1424
return true;
1425
}
1426
1427
// Step 3 (omitted).
1428
1429
// Step 4.
1430
HandlePropertyName funName = cx->names().empty;
1431
RootedFunction executor(
1432
cx, NewNativeFunction(cx, GetCapabilitiesExecutor, 2, funName,
1433
gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1434
if (!executor) {
1435
return false;
1436
}
1437
1438
// Step 5 (omitted).
1439
1440
// Step 6.
1441
FixedConstructArgs<1> cargs(cx);
1442
cargs[0].setObject(*executor);
1443
if (!Construct(cx, cVal, cargs, cVal, capability.promise())) {
1444
return false;
1445
}
1446
1447
// Step 7.
1448
const Value& resolveVal =
1449
executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve);
1450
if (!IsCallable(resolveVal)) {
1451
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1452
JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE);
1453
return false;
1454
}
1455
1456
// Step 8.
1457
const Value& rejectVal =
1458
executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject);
1459
if (!IsCallable(rejectVal)) {
1460
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1461
JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE);
1462
return false;
1463
}
1464
1465
// Step 9 (well, the equivalent for all of promiseCapabilities' fields.)
1466
capability.resolve().set(&resolveVal.toObject());
1467
capability.reject().set(&rejectVal.toObject());
1468
1469
// Step 10.
1470
return true;
1471
}
1472
1473
// ES2016, 25.4.1.5.1.
1474
static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp) {
1475
CallArgs args = CallArgsFromVp(argc, vp);
1476
JSFunction* F = &args.callee().as<JSFunction>();
1477
1478
// Steps 1-2 (implicit).
1479
1480
// Steps 3-4.
1481
if (!F->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve).isUndefined() ||
1482
!F->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject).isUndefined()) {
1483
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1484
JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY);
1485
return false;
1486
}
1487
1488
// Step 5.
1489
F->setExtendedSlot(GetCapabilitiesExecutorSlots_Resolve, args.get(0));
1490
1491
// Step 6.
1492
F->setExtendedSlot(GetCapabilitiesExecutorSlots_Reject, args.get(1));
1493
1494
// Step 7.
1495
args.rval().setUndefined();
1496
return true;
1497
}
1498
1499
// ES2016, 25.4.1.7.
1500
static MOZ_MUST_USE bool RejectMaybeWrappedPromise(JSContext* cx,
1501
HandleObject promiseObj,
1502
HandleValue reason_) {
1503
Rooted<PromiseObject*> promise(cx);
1504
RootedValue reason(cx, reason_);
1505
1506
mozilla::Maybe<AutoRealm> ar;
1507
if (!IsProxy(promiseObj)) {
1508
promise = &promiseObj->as<PromiseObject>();
1509
} else {
1510
JSObject* unwrappedPromiseObj = UncheckedUnwrap(promiseObj);
1511
if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
1512
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1513
JSMSG_DEAD_OBJECT);
1514
return false;
1515
}
1516
promise = &unwrappedPromiseObj->as<PromiseObject>();
1517
ar.emplace(cx, promise);
1518
1519
// The rejection reason might've been created in a compartment with higher
1520
// privileges than the Promise's. In that case, object-type rejection
1521
// values might be wrapped into a wrapper that throws whenever the
1522
// Promise's reaction handler wants to do anything useful with it. To
1523
// avoid that situation, we synthesize a generic error that doesn't
1524
// expose any privileged information but can safely be used in the
1525
// rejection handler.
1526
if (!cx->compartment()->wrap(cx, &reason)) {
1527
return false;
1528
}
1529
if (reason.isObject() && !CheckedUnwrapStatic(&reason.toObject())) {
1530
// Report the existing reason, so we don't just drop it on the
1531
// floor.
1532
JSObject* realReason = UncheckedUnwrap(&reason.toObject());
1533
RootedValue realReasonVal(cx, ObjectValue(*realReason));
1534
Rooted<GlobalObject*> realGlobal(cx, &realReason->nonCCWGlobal());
1535
ReportErrorToGlobal(cx, realGlobal, realReasonVal);
1536
1537
// Async stacks are only properly adopted if there's at least one
1538
// interpreter frame active right now. If a thenable job with a
1539
// throwing `then` function got us here, that'll not be the case,
1540
// so we add one by throwing the error from self-hosted code.
1541
if (!GetInternalError(cx, JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,
1542
&reason)) {
1543
return false;
1544
}
1545
}
1546
}
1547
1548
return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected);
1549
}
1550
1551
// Apply f to a mutable handle on each member of a collection of reactions, like
1552
// that stored in PromiseSlot_ReactionsOrResult on a pending promise. When the
1553
// reaction record is wrapped, we pass the wrapper, without dereferencing it. If
1554
// f returns false, then we stop the iteration immediately and return false.
1555
// Otherwise, we return true.
1556
//
1557
// There are several different representations for collections:
1558
//
1559
// - We represent an empty collection of reactions as an 'undefined' value.
1560
//
1561
// - We represent a collection containing a single reaction simply as the given
1562
// PromiseReactionRecord object, possibly wrapped.
1563
//
1564
// - We represent a collection of two or more reactions as a dense array of
1565
// possibly-wrapped PromiseReactionRecords.
1566
//
1567
template <typename F>
1568
static bool ForEachReaction(JSContext* cx, HandleValue reactionsVal, F f) {
1569
if (reactionsVal.isUndefined()) {
1570
return true;
1571
}
1572
1573
RootedObject reactions(cx, &reactionsVal.toObject());
1574
RootedObject reaction(cx);
1575
1576
if (reactions->is<PromiseReactionRecord>() || IsWrapper(reactions) ||
1577
JS_IsDeadWrapper(reactions)) {
1578
return f(&reactions);
1579
}
1580
1581
HandleNativeObject reactionsList = reactions.as<NativeObject>();
1582
uint32_t reactionsCount = reactionsList->getDenseInitializedLength();
1583
MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily");
1584
1585
for (uint32_t i = 0; i < reactionsCount; i++) {
1586
const Value& reactionVal = reactionsList->getDenseElement(i);
1587
MOZ_RELEASE_ASSERT(reactionVal.isObject());
1588
reaction = &reactionVal.toObject();
1589
if (!f(&reaction)) {
1590
return false;
1591
}
1592
}
1593
1594
return true;
1595
}
1596
1597
// ES2016, 25.4.1.8.
1598
static MOZ_MUST_USE bool TriggerPromiseReactions(JSContext* cx,
1599
HandleValue reactionsVal,
1600
JS::PromiseState state,
1601
HandleValue valueOrReason) {
1602
MOZ_ASSERT(state == JS::PromiseState::Fulfilled ||
1603
state == JS::PromiseState::Rejected);
1604
1605
return ForEachReaction(cx, reactionsVal, [&](MutableHandleObject reaction) {
1606
return EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state);
1607
});
1608
}
1609
1610
static MOZ_MUST_USE bool RunFulfillFunction(JSContext* cx,
1611
HandleObject onFulfilledFunc,
1612
HandleValue result,
1613
HandleObject promiseObj);
1614
1615
// Implements PromiseReactionJob optimized for the case when the reaction
1616
// handler is one of the default resolving functions as created by the
1617
// CreateResolvingFunctions abstract operation.
1618
static MOZ_MUST_USE bool DefaultResolvingPromiseReactionJob(
1619
JSContext* cx, Handle<PromiseReactionRecord*> reaction) {
1620
MOZ_ASSERT(reaction->targetState() != JS::PromiseState::Pending);
1621
1622
Rooted<PromiseObject*> promiseToResolve(cx,
1623
reaction->defaultResolvingPromise());
1624
1625
// Testing functions allow to directly settle a promise without going
1626
// through the resolving functions. In that case the normal bookkeeping to
1627
// ensure only pending promises can be resolved doesn't apply and we need
1628
// to manually check for already settled promises. We still call
1629
// Run{Fulfill,Reject}Function for consistency with PromiseReactionJob.
1630
ResolutionMode resolutionMode = ResolveMode;
1631
RootedValue handlerResult(cx, UndefinedValue());
1632
if (promiseToResolve->state() == JS::PromiseState::Pending) {
1633
RootedValue argument(cx, reaction->handlerArg());
1634
1635
// Step 6.
1636
bool ok;
1637
if (reaction->targetState() == JS::PromiseState::Fulfilled) {
1638
ok = ResolvePromiseInternal(cx, promiseToResolve, argument);
1639
} else {
1640
ok = RejectPromiseInternal(cx, promiseToResolve, argument);
1641
}
1642
1643
if (!ok) {
1644
resolutionMode = RejectMode;
1645
if (!MaybeGetAndClearException(cx, &handlerResult)) {
1646
return false;
1647
}
1648
}
1649
}
1650
1651
// Steps 7-9.
1652
RootedObject promiseObj(cx, reaction->promise());
1653
RootedObject callee(cx);
1654
if (resolutionMode == ResolveMode) {
1655
callee =
1656
reaction->getFixedSlot(ReactionRecordSlot_Resolve).toObjectOrNull();
1657
1658
return RunFulfillFunction(cx, callee, handlerResult, promiseObj);
1659
}
1660
1661
callee = reaction->getFixedSlot(ReactionRecordSlot_Reject).toObjectOrNull();
1662
1663
return RunRejectFunction(cx, callee, handlerResult, promiseObj,
1664
reaction->unhandledRejectionBehavior());
1665
}
1666
1667
static MOZ_MUST_USE bool AsyncFunctionPromiseReactionJob(
1668
JSContext* cx, Handle<PromiseReactionRecord*> reaction) {
1669
MOZ_ASSERT(reaction->isAsyncFunction());
1670
1671
int32_t handler = reaction->handler().toInt32();
1672
RootedValue argument(cx, reaction->handlerArg());
1673
Rooted<AsyncFunctionGeneratorObject*> generator(
1674
cx, reaction->asyncFunctionGenerator());
1675
1676
// Await's handlers don't return a value, nor throw any exceptions.
1677
// They fail only on OOM.
1678
1679
if (handler == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
1680
return AsyncFunctionAwaitedFulfilled(cx, generator, argument);
1681
}
1682
1683
MOZ_ASSERT(handler == PromiseHandlerAsyncFunctionAwaitedRejected);
1684
return AsyncFunctionAwaitedRejected(cx, generator, argument);
1685
}
1686
1687
static MOZ_MUST_USE bool AsyncGeneratorPromiseReactionJob(
1688
JSContext* cx, Handle<PromiseReactionRecord*> reaction) {
1689
MOZ_ASSERT(reaction->isAsyncGenerator());
1690
1691
int32_t handler = reaction->handler().toInt32();
1692
RootedValue argument(cx, reaction->handlerArg());
1693
Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator());
1694
1695
// Await's handlers don't return a value, nor throw any exceptions.
1696
// They fail only on OOM.
1697
1698
switch (handler) {
1699
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1700
// 6.2.3.1.1 Await Fulfilled Functions
1701
case PromiseHandlerAsyncGeneratorAwaitedFulfilled: {
1702
MOZ_ASSERT(asyncGenObj->isExecuting(),
1703
"Await fulfilled when not in 'Executing' state");
1704
1705
return AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument);
1706
}
1707
1708
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1709
// 6.2.3.1.2 Await Rejected Functions
1710
case PromiseHandlerAsyncGeneratorAwaitedRejected: {
1711
MOZ_ASSERT(asyncGenObj->isExecuting(),
1712
"Await rejected when not in 'Executing' state");
1713
1714
return AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument);
1715
}
1716
1717
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1718
// 25.5.3.5.1 AsyncGeneratorResumeNext Return Processor Fulfilled Functions
1719
case PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled: {
1720
MOZ_ASSERT(asyncGenObj->isAwaitingReturn(),
1721
"AsyncGeneratorResumeNext-Return fulfilled when not in "
1722
"'AwaitingReturn' state");
1723
1724
// Steps 1-2.
1725
asyncGenObj->setCompleted();
1726
1727
// Step 3.
1728
return AsyncGeneratorResolve(cx, asyncGenObj, argument, true);
1729
}
1730
1731
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1732
// 25.5.3.5.2 AsyncGeneratorResumeNext Return Processor Rejected Functions
1733
case PromiseHandlerAsyncGeneratorResumeNextReturnRejected: {
1734
MOZ_ASSERT(asyncGenObj->isAwaitingReturn(),
1735
"AsyncGeneratorResumeNext-Return rejected when not in "
1736
"'AwaitingReturn' state");
1737
1738
// Steps 1-2.
1739
asyncGenObj->setCompleted();
1740
1741
// Step 3.
1742
return AsyncGeneratorReject(cx, asyncGenObj, argument);
1743
}
1744
1745
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1746
// 25.5.3.7 AsyncGeneratorYield
1747
case PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled: {
1748
MOZ_ASSERT(asyncGenObj->isAwaitingYieldReturn(),
1749
"YieldReturn-Await fulfilled when not in "
1750
"'AwaitingYieldReturn' state");
1751
1752
// We're using a separate 'AwaitingYieldReturn' state when awaiting a
1753
// return completion in yield expressions, whereas the spec uses the
1754
// 'Executing' state all along. So we now need to transition into the
1755
// 'Executing' state.
1756
asyncGenObj->setExecuting();
1757
1758
// Steps 8.d-e.
1759
return AsyncGeneratorYieldReturnAwaitedFulfilled(cx, asyncGenObj,
1760
argument);
1761
}
1762
1763
// ES2020 draft rev a09fc232c137800dbf51b6204f37fdede4ba1646
1764
// 25.5.3.7 AsyncGeneratorYield
1765
case PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected: {
1766
MOZ_ASSERT(
1767
asyncGenObj->isAwaitingYieldReturn(),
1768
"YieldReturn-Await rejected when not in 'AwaitingYieldReturn' state");
1769
1770
// We're using a separate 'AwaitingYieldReturn' state when awaiting a
1771
// return completion in yield expressions, whereas the spec uses the
1772
// 'Executing' state all along. So we now need to transition into the
1773
// 'Executing' state.
1774
asyncGenObj->setExecuting();
1775
1776
// Step 8.c.
1777
return AsyncGeneratorYieldReturnAwaitedRejected(cx, asyncGenObj,
1778
argument);
1779
}
1780
1781
default:
1782
MOZ_CRASH("Bad handler in AsyncGeneratorPromiseReactionJob");
1783
}
1784
}
1785
1786
// ES2016, 25.4.2.1.
1787
/**
1788
* Callback triggering the fulfill/reject reaction for a resolved Promise,
1789
* to be invoked by the embedding during its processing of the Promise job
1790
* queue.
1791
*
1792
* See
179