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