Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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 :
8 : #include "builtin/Promise.h"
9 :
10 : #include "mozilla/Atomics.h"
11 : #include "mozilla/TimeStamp.h"
12 :
13 : #include "jsexn.h"
14 : #include "jsfriendapi.h"
15 :
16 : #include "gc/Heap.h"
17 : #include "js/Debug.h"
18 : #include "vm/AsyncFunction.h"
19 : #include "vm/AsyncIteration.h"
20 : #include "vm/Debugger.h"
21 : #include "vm/Iteration.h"
22 : #include "vm/JSContext.h"
23 :
24 : #include "vm/Compartment-inl.h"
25 : #include "vm/Debugger-inl.h"
26 : #include "vm/JSObject-inl.h"
27 : #include "vm/NativeObject-inl.h"
28 :
29 : using namespace js;
30 :
31 : static double
32 8513 : MillisecondsSinceStartup()
33 : {
34 0 : auto now = mozilla::TimeStamp::Now();
35 17026 : return (now - mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
36 : }
37 :
38 : enum PromiseHandler {
39 : PromiseHandlerIdentity = 0,
40 : PromiseHandlerThrower,
41 :
42 : // ES 2018 draft 25.5.5.4-5.
43 : PromiseHandlerAsyncFunctionAwaitedFulfilled,
44 : PromiseHandlerAsyncFunctionAwaitedRejected,
45 :
46 : // Async Iteration proposal 4.1.
47 : PromiseHandlerAsyncGeneratorAwaitedFulfilled,
48 : PromiseHandlerAsyncGeneratorAwaitedRejected,
49 :
50 : // Async Iteration proposal 11.4.3.5.1-2.
51 : PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled,
52 : PromiseHandlerAsyncGeneratorResumeNextReturnRejected,
53 :
54 : // Async Iteration proposal 11.4.3.7 steps 8.c-e.
55 : PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled,
56 : PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected,
57 :
58 : // Async Iteration proposal 11.1.3.2.5.
59 : // Async-from-Sync iterator handlers take the resolved value and create new
60 : // iterator objects. To do so it needs to forward whether the iterator is
61 : // done. In spec, this is achieved via the [[Done]] internal slot. We
62 : // enumerate both true and false cases here.
63 : PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone,
64 : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone,
65 : };
66 :
67 : enum ResolutionMode {
68 : ResolveMode,
69 : RejectMode
70 : };
71 :
72 : enum ResolveFunctionSlots {
73 : ResolveFunctionSlot_Promise = 0,
74 : ResolveFunctionSlot_RejectFunction,
75 : };
76 :
77 : enum RejectFunctionSlots {
78 : RejectFunctionSlot_Promise = 0,
79 : RejectFunctionSlot_ResolveFunction,
80 : };
81 :
82 : enum PromiseAllResolveElementFunctionSlots {
83 : PromiseAllResolveElementFunctionSlot_Data = 0,
84 : PromiseAllResolveElementFunctionSlot_ElementIndex,
85 : };
86 :
87 : enum ReactionJobSlots {
88 : ReactionJobSlot_ReactionRecord = 0,
89 : };
90 :
91 : enum ThenableJobSlots {
92 : ThenableJobSlot_Handler = 0,
93 : ThenableJobSlot_JobData,
94 : };
95 :
96 : enum ThenableJobDataIndices {
97 : ThenableJobDataIndex_Promise = 0,
98 : ThenableJobDataIndex_Thenable,
99 : ThenableJobDataLength,
100 : };
101 :
102 : enum PromiseAllDataHolderSlots {
103 : PromiseAllDataHolderSlot_Promise = 0,
104 : PromiseAllDataHolderSlot_RemainingElements,
105 : PromiseAllDataHolderSlot_ValuesArray,
106 : PromiseAllDataHolderSlot_ResolveFunction,
107 : PromiseAllDataHolderSlots,
108 : };
109 :
110 : class PromiseAllDataHolder : public NativeObject
111 : {
112 : public:
113 : static const Class class_;
114 74 : JSObject* promiseObj() { return &getFixedSlot(PromiseAllDataHolderSlot_Promise).toObject(); }
115 : JSObject* resolveObj() {
116 74 : return &getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObject();
117 : }
118 229 : Value valuesArray() { return getFixedSlot(PromiseAllDataHolderSlot_ValuesArray); }
119 : int32_t remainingCount() {
120 : return getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
121 : }
122 0 : int32_t increaseRemainingCount() {
123 0 : int32_t remainingCount = getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
124 0 : remainingCount++;
125 0 : setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(remainingCount));
126 230 : return remainingCount;
127 : }
128 0 : int32_t decreaseRemainingCount() {
129 0 : int32_t remainingCount = getFixedSlot(PromiseAllDataHolderSlot_RemainingElements).toInt32();
130 0 : remainingCount--;
131 0 : setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(remainingCount));
132 314 : return remainingCount;
133 : }
134 : };
135 :
136 : const Class PromiseAllDataHolder::class_ = {
137 : "PromiseAllDataHolder",
138 : JSCLASS_HAS_RESERVED_SLOTS(PromiseAllDataHolderSlots)
139 : };
140 :
141 : static PromiseAllDataHolder*
142 85 : NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue valuesArray,
143 : HandleObject resolve)
144 : {
145 0 : Rooted<PromiseAllDataHolder*> dataHolder(cx, NewObjectWithClassProto<PromiseAllDataHolder>(cx));
146 85 : if (!dataHolder)
147 : return nullptr;
148 :
149 0 : assertSameCompartment(cx, resultPromise);
150 0 : assertSameCompartment(cx, valuesArray);
151 85 : assertSameCompartment(cx, resolve);
152 :
153 0 : dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise));
154 0 : dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1));
155 0 : dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArray);
156 0 : dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectValue(*resolve));
157 85 : return dataHolder;
158 : }
159 :
160 : namespace {
161 : // Generator used by PromiseObject::getID.
162 : mozilla::Atomic<uint64_t> gIDGenerator(0);
163 : } // namespace
164 :
165 : static MOZ_ALWAYS_INLINE bool
166 8513 : ShouldCaptureDebugInfo(JSContext* cx)
167 : {
168 17026 : return cx->options().asyncStack() || cx->realm()->isDebuggee();
169 : }
170 :
171 : class PromiseDebugInfo : public NativeObject
172 : {
173 : private:
174 : enum Slots {
175 : Slot_AllocationSite,
176 : Slot_ResolutionSite,
177 : Slot_AllocationTime,
178 : Slot_ResolutionTime,
179 : Slot_Id,
180 : SlotCount
181 : };
182 :
183 : public:
184 : static const Class class_;
185 0 : static PromiseDebugInfo* create(JSContext* cx, Handle<PromiseObject*> promise) {
186 0 : Rooted<PromiseDebugInfo*> debugInfo(cx, NewObjectWithClassProto<PromiseDebugInfo>(cx));
187 4410 : if (!debugInfo)
188 : return nullptr;
189 :
190 0 : RootedObject stack(cx);
191 13230 : if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames())))
192 : return nullptr;
193 0 : debugInfo->setFixedSlot(Slot_AllocationSite, ObjectOrNullValue(stack));
194 0 : debugInfo->setFixedSlot(Slot_ResolutionSite, NullValue());
195 0 : debugInfo->setFixedSlot(Slot_AllocationTime, DoubleValue(MillisecondsSinceStartup()));
196 0 : debugInfo->setFixedSlot(Slot_ResolutionTime, NumberValue(0));
197 13230 : promise->setFixedSlot(PromiseSlot_DebugInfo, ObjectValue(*debugInfo));
198 :
199 4410 : return debugInfo;
200 : }
201 :
202 0 : static PromiseDebugInfo* FromPromise(PromiseObject* promise) {
203 0 : Value val = promise->getFixedSlot(PromiseSlot_DebugInfo);
204 0 : if (val.isObject())
205 10143 : return &val.toObject().as<PromiseDebugInfo>();
206 : return nullptr;
207 : }
208 :
209 : /**
210 : * Returns the given PromiseObject's process-unique ID.
211 : * The ID is lazily assigned when first queried, and then either stored
212 : * in the DebugInfo slot if no debug info was recorded for this Promise,
213 : * or in the Id slot of the DebugInfo object.
214 : */
215 0 : static uint64_t id(PromiseObject* promise) {
216 0 : Value idVal(promise->getFixedSlot(PromiseSlot_DebugInfo));
217 0 : if (idVal.isUndefined()) {
218 0 : idVal.setDouble(++gIDGenerator);
219 0 : promise->setFixedSlot(PromiseSlot_DebugInfo, idVal);
220 0 : } else if (idVal.isObject()) {
221 0 : PromiseDebugInfo* debugInfo = FromPromise(promise);
222 0 : idVal = debugInfo->getFixedSlot(Slot_Id);
223 0 : if (idVal.isUndefined()) {
224 0 : idVal.setDouble(++gIDGenerator);
225 0 : debugInfo->setFixedSlot(Slot_Id, idVal);
226 : }
227 : }
228 0 : return uint64_t(idVal.toNumber());
229 : }
230 :
231 0 : double allocationTime() { return getFixedSlot(Slot_AllocationTime).toNumber(); }
232 0 : double resolutionTime() { return getFixedSlot(Slot_ResolutionTime).toNumber(); }
233 0 : JSObject* allocationSite() { return getFixedSlot(Slot_AllocationSite).toObjectOrNull(); }
234 0 : JSObject* resolutionSite() { return getFixedSlot(Slot_ResolutionSite).toObjectOrNull(); }
235 :
236 0 : static void setResolutionInfo(JSContext* cx, Handle<PromiseObject*> promise) {
237 0 : if (!ShouldCaptureDebugInfo(cx))
238 0 : return;
239 :
240 : // If async stacks weren't enabled and the Promise's global wasn't a
241 : // debuggee when the Promise was created, we won't have a debugInfo
242 : // object. We still want to capture the resolution stack, so we
243 : // create the object now and change it's slots' values around a bit.
244 0 : Rooted<PromiseDebugInfo*> debugInfo(cx, FromPromise(promise));
245 0 : if (!debugInfo) {
246 0 : RootedValue idVal(cx, promise->getFixedSlot(PromiseSlot_DebugInfo));
247 0 : debugInfo = create(cx, promise);
248 0 : if (!debugInfo) {
249 0 : cx->clearPendingException();
250 0 : return;
251 : }
252 :
253 : // The current stack was stored in the AllocationSite slot, move
254 : // it to ResolutionSite as that's what it really is.
255 0 : debugInfo->setFixedSlot(Slot_ResolutionSite,
256 0 : debugInfo->getFixedSlot(Slot_AllocationSite));
257 0 : debugInfo->setFixedSlot(Slot_AllocationSite, NullValue());
258 :
259 : // There's no good default for a missing AllocationTime, so
260 : // instead of resetting that, ensure that it's the same as
261 : // ResolutionTime, so that the diff shows as 0, which isn't great,
262 : // but bearable.
263 0 : debugInfo->setFixedSlot(Slot_ResolutionTime,
264 0 : debugInfo->getFixedSlot(Slot_AllocationTime));
265 :
266 : // The Promise's ID might've been queried earlier, in which case
267 : // it's stored in the DebugInfo slot. We saved that earlier, so
268 : // now we can store it in the right place (or leave it as
269 : // undefined if it wasn't ever initialized.)
270 0 : debugInfo->setFixedSlot(Slot_Id, idVal);
271 0 : return;
272 : }
273 :
274 0 : RootedObject stack(cx);
275 1 : if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames()))) {
276 0 : cx->clearPendingException();
277 0 : return;
278 : }
279 :
280 0 : debugInfo->setFixedSlot(Slot_ResolutionSite, ObjectOrNullValue(stack));
281 12309 : debugInfo->setFixedSlot(Slot_ResolutionTime, DoubleValue(MillisecondsSinceStartup()));
282 : }
283 : };
284 :
285 : const Class PromiseDebugInfo::class_ = {
286 : "PromiseDebugInfo",
287 : JSCLASS_HAS_RESERVED_SLOTS(SlotCount)
288 : };
289 :
290 : double
291 0 : PromiseObject::allocationTime()
292 : {
293 0 : auto debugInfo = PromiseDebugInfo::FromPromise(this);
294 0 : if (debugInfo)
295 0 : return debugInfo->allocationTime();
296 : return 0;
297 : }
298 :
299 : double
300 0 : PromiseObject::resolutionTime()
301 : {
302 0 : auto debugInfo = PromiseDebugInfo::FromPromise(this);
303 0 : if (debugInfo)
304 0 : return debugInfo->resolutionTime();
305 : return 0;
306 : }
307 :
308 : JSObject*
309 6040 : PromiseObject::allocationSite()
310 : {
311 0 : auto debugInfo = PromiseDebugInfo::FromPromise(this);
312 0 : if (debugInfo)
313 6040 : return debugInfo->allocationSite();
314 : return nullptr;
315 : }
316 :
317 : JSObject*
318 0 : PromiseObject::resolutionSite()
319 : {
320 0 : auto debugInfo = PromiseDebugInfo::FromPromise(this);
321 0 : if (debugInfo)
322 0 : return debugInfo->resolutionSite();
323 : return nullptr;
324 : }
325 :
326 : /**
327 : * Wrapper for GetAndClearException that handles cases where no exception is
328 : * pending, but an error occurred. This can be the case if an OOM was
329 : * encountered while throwing the error.
330 : */
331 : static bool
332 20 : MaybeGetAndClearException(JSContext* cx, MutableHandleValue rval)
333 : {
334 20 : if (!cx->isExceptionPending())
335 : return false;
336 :
337 20 : return GetAndClearException(cx, rval);
338 : }
339 :
340 : static MOZ_MUST_USE bool RunResolutionFunction(JSContext *cx, HandleObject resolutionFun,
341 : HandleValue result, ResolutionMode mode,
342 : HandleObject promiseObj);
343 :
344 : // ES2016, 25.4.1.1.1, Steps 1.a-b.
345 : // Extracting all of this internal spec algorithm into a helper function would
346 : // be tedious, so the check in step 1 and the entirety of step 2 aren't
347 : // included.
348 : static bool
349 0 : AbruptRejectPromise(JSContext *cx, CallArgs& args, HandleObject promiseObj, HandleObject reject)
350 : {
351 : // Step 1.a.
352 0 : RootedValue reason(cx);
353 0 : if (!MaybeGetAndClearException(cx, &reason))
354 : return false;
355 :
356 0 : if (!RunResolutionFunction(cx, reject, reason, RejectMode, promiseObj))
357 : return false;
358 :
359 : // Step 1.b.
360 0 : args.rval().setObject(*promiseObj);
361 0 : return true;
362 : }
363 :
364 : enum ReactionRecordSlots {
365 : ReactionRecordSlot_Promise = 0,
366 : ReactionRecordSlot_OnFulfilled,
367 : ReactionRecordSlot_OnRejected,
368 : ReactionRecordSlot_Resolve,
369 : ReactionRecordSlot_Reject,
370 : ReactionRecordSlot_IncumbentGlobalObject,
371 : ReactionRecordSlot_Flags,
372 : ReactionRecordSlot_HandlerArg,
373 : ReactionRecordSlot_Generator,
374 : ReactionRecordSlots,
375 : };
376 :
377 : #define REACTION_FLAG_RESOLVED 0x1
378 : #define REACTION_FLAG_FULFILLED 0x2
379 : #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4
380 : #define REACTION_FLAG_ASYNC_FUNCTION 0x8
381 : #define REACTION_FLAG_ASYNC_GENERATOR 0x10
382 :
383 : // ES2016, 25.4.1.2.
384 : class PromiseReactionRecord : public NativeObject
385 : {
386 : public:
387 : static const Class class_;
388 :
389 0 : JSObject* promise() { return getFixedSlot(ReactionRecordSlot_Promise).toObjectOrNull(); }
390 0 : int32_t flags() { return getFixedSlot(ReactionRecordSlot_Flags).toInt32(); }
391 0 : JS::PromiseState targetState() {
392 0 : int32_t flags = this->flags();
393 20412 : if (!(flags & REACTION_FLAG_RESOLVED))
394 : return JS::PromiseState::Pending;
395 0 : return flags & REACTION_FLAG_FULFILLED
396 14580 : ? JS::PromiseState::Fulfilled
397 : : JS::PromiseState::Rejected;
398 : }
399 0 : void setTargetState(JS::PromiseState state) {
400 0 : int32_t flags = this->flags();
401 0 : MOZ_ASSERT(!(flags & REACTION_FLAG_RESOLVED));
402 0 : MOZ_ASSERT(state != JS::PromiseState::Pending, "Can't revert a reaction to pending.");
403 0 : flags |= REACTION_FLAG_RESOLVED;
404 0 : if (state == JS::PromiseState::Fulfilled)
405 0 : flags |= REACTION_FLAG_FULFILLED;
406 0 : setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
407 0 : }
408 0 : void setIsAsyncFunction() {
409 0 : int32_t flags = this->flags();
410 0 : flags |= REACTION_FLAG_ASYNC_FUNCTION;
411 0 : setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
412 0 : }
413 0 : bool isAsyncFunction() {
414 0 : int32_t flags = this->flags();
415 3950 : return flags & REACTION_FLAG_ASYNC_FUNCTION;
416 : }
417 0 : void setIsAsyncGenerator(Handle<AsyncGeneratorObject*> asyncGenObj) {
418 0 : int32_t flags = this->flags();
419 0 : flags |= REACTION_FLAG_ASYNC_GENERATOR;
420 0 : setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
421 :
422 0 : setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj));
423 0 : }
424 0 : bool isAsyncGenerator() {
425 0 : int32_t flags = this->flags();
426 1882 : return flags & REACTION_FLAG_ASYNC_GENERATOR;
427 : }
428 0 : AsyncGeneratorObject* asyncGenerator() {
429 0 : MOZ_ASSERT(isAsyncGenerator());
430 0 : return &getFixedSlot(ReactionRecordSlot_Generator).toObject()
431 0 : .as<AsyncGeneratorObject>();
432 : }
433 0 : Value handler() {
434 0 : MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
435 0 : uint32_t slot = targetState() == JS::PromiseState::Fulfilled
436 0 : ? ReactionRecordSlot_OnFulfilled
437 0 : : ReactionRecordSlot_OnRejected;
438 5832 : return getFixedSlot(slot);
439 : }
440 0 : Value handlerArg() {
441 0 : MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
442 2916 : return getFixedSlot(ReactionRecordSlot_HandlerArg);
443 : }
444 0 : void setHandlerArg(Value& arg) {
445 0 : MOZ_ASSERT(targetState() == JS::PromiseState::Pending);
446 0 : setFixedSlot(ReactionRecordSlot_HandlerArg, arg);
447 2916 : }
448 : JSObject* incumbentGlobalObject() {
449 2916 : return getFixedSlot(ReactionRecordSlot_IncumbentGlobalObject).toObjectOrNull();
450 : }
451 : };
452 :
453 : const Class PromiseReactionRecord::class_ = {
454 : "PromiseReactionRecord",
455 : JSCLASS_HAS_RESERVED_SLOTS(ReactionRecordSlots)
456 : };
457 :
458 : static void
459 4905 : AddPromiseFlags(PromiseObject& promise, int32_t flag)
460 : {
461 0 : int32_t flags = promise.getFixedSlot(PromiseSlot_Flags).toInt32();
462 0 : promise.setFixedSlot(PromiseSlot_Flags, Int32Value(flags | flag));
463 4905 : }
464 :
465 : static bool
466 2051 : PromiseHasAnyFlag(PromiseObject& promise, int32_t flag)
467 : {
468 2051 : return promise.getFixedSlot(PromiseSlot_Flags).toInt32() & flag;
469 : }
470 :
471 : static bool ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp);
472 : static bool RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp);
473 :
474 : // ES2016, 25.4.1.3.
475 : static MOZ_MUST_USE MOZ_ALWAYS_INLINE bool
476 1690 : CreateResolvingFunctions(JSContext* cx, HandleObject promise,
477 : MutableHandleObject resolveFn,
478 : MutableHandleObject rejectFn)
479 : {
480 0 : HandlePropertyName funName = cx->names().empty;
481 0 : resolveFn.set(NewNativeFunction(cx, ResolvePromiseFunction, 1, funName,
482 0 : gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
483 1690 : if (!resolveFn)
484 : return false;
485 :
486 0 : rejectFn.set(NewNativeFunction(cx, RejectPromiseFunction, 1, funName,
487 0 : gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
488 1690 : if (!rejectFn)
489 : return false;
490 :
491 0 : JSFunction* resolveFun = &resolveFn->as<JSFunction>();
492 1690 : JSFunction* rejectFun = &rejectFn->as<JSFunction>();
493 :
494 0 : resolveFun->initExtendedSlot(ResolveFunctionSlot_Promise, ObjectValue(*promise));
495 3380 : resolveFun->initExtendedSlot(ResolveFunctionSlot_RejectFunction, ObjectValue(*rejectFun));
496 :
497 0 : rejectFun->initExtendedSlot(RejectFunctionSlot_Promise, ObjectValue(*promise));
498 3380 : rejectFun->initExtendedSlot(RejectFunctionSlot_ResolveFunction, ObjectValue(*resolveFun));
499 :
500 1690 : return true;
501 : }
502 :
503 : static void ClearResolutionFunctionSlots(JSFunction* resolutionFun);
504 : static MOZ_MUST_USE bool RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj,
505 : HandleValue reason);
506 :
507 : // ES2016, 25.4.1.3.1.
508 : static bool
509 66 : RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp)
510 : {
511 66 : CallArgs args = CallArgsFromVp(argc, vp);
512 :
513 0 : RootedFunction reject(cx, &args.callee().as<JSFunction>());
514 132 : RootedValue reasonVal(cx, args.get(0));
515 :
516 : // Steps 1-2.
517 132 : RootedValue promiseVal(cx, reject->getExtendedSlot(RejectFunctionSlot_Promise));
518 :
519 : // Steps 3-4.
520 : // If the Promise isn't available anymore, it has been resolved and the
521 : // reference to it removed to make it eligible for collection.
522 0 : if (promiseVal.isUndefined()) {
523 0 : args.rval().setUndefined();
524 0 : return true;
525 : }
526 :
527 : // Step 5.
528 : // Here, we only remove the Promise reference from the resolution
529 : // functions. Actually marking it as fulfilled/rejected happens later.
530 66 : ClearResolutionFunctionSlots(reject);
531 :
532 132 : RootedObject promise(cx, &promiseVal.toObject());
533 :
534 : // In some cases the Promise reference on the resolution function won't
535 : // have been removed during resolution, so we need to check that here,
536 : // too.
537 0 : if (promise->is<PromiseObject>() &&
538 66 : promise->as<PromiseObject>().state() != JS::PromiseState::Pending)
539 : {
540 : return true;
541 : }
542 :
543 : // Step 6.
544 132 : if (!RejectMaybeWrappedPromise(cx, promise, reasonVal))
545 : return false;
546 0 : args.rval().setUndefined();
547 66 : return true;
548 : }
549 :
550 : static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj,
551 : HandleValue value_);
552 :
553 : static MOZ_MUST_USE bool EnqueuePromiseResolveThenableJob(JSContext* cx,
554 : HandleValue promiseToResolve,
555 : HandleValue thenable,
556 : HandleValue thenVal);
557 :
558 : // ES2016, 25.4.1.3.2, steps 6-13.
559 : static MOZ_MUST_USE bool
560 5223 : ResolvePromiseInternal(JSContext* cx, HandleObject promise, HandleValue resolutionVal)
561 : {
562 : // Step 7 (reordered).
563 0 : if (!resolutionVal.isObject())
564 2129 : return FulfillMaybeWrappedPromise(cx, promise, resolutionVal);
565 :
566 6188 : RootedObject resolution(cx, &resolutionVal.toObject());
567 :
568 : // Step 6.
569 3094 : if (resolution == promise) {
570 : // Step 6.a.
571 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
572 0 : JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF);
573 0 : RootedValue selfResolutionError(cx);
574 0 : if (!MaybeGetAndClearException(cx, &selfResolutionError))
575 : return false;
576 :
577 : // Step 6.b.
578 0 : return RejectMaybeWrappedPromise(cx, promise, selfResolutionError);
579 : }
580 :
581 : // Step 8.
582 0 : RootedValue thenVal(cx);
583 15470 : bool status = GetProperty(cx, resolution, resolution, cx->names().then, &thenVal);
584 :
585 : // Step 9.
586 0 : if (!status) {
587 0 : RootedValue error(cx);
588 0 : if (!MaybeGetAndClearException(cx, &error))
589 : return false;
590 :
591 0 : return RejectMaybeWrappedPromise(cx, promise, error);
592 : }
593 :
594 : // Step 10 (implicit).
595 :
596 : // Step 11.
597 0 : if (!IsCallable(thenVal))
598 1862 : return FulfillMaybeWrappedPromise(cx, promise, resolutionVal);
599 :
600 : // Step 12.
601 0 : RootedValue promiseVal(cx, ObjectValue(*promise));
602 2464 : if (!EnqueuePromiseResolveThenableJob(cx, promiseVal, resolutionVal, thenVal))
603 : return false;
604 :
605 : // Step 13.
606 1232 : return true;
607 : }
608 :
609 : // ES2016, 25.4.1.3.2.
610 : static bool
611 1503 : ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
612 : {
613 1503 : CallArgs args = CallArgsFromVp(argc, vp);
614 :
615 0 : RootedFunction resolve(cx, &args.callee().as<JSFunction>());
616 3006 : RootedValue resolutionVal(cx, args.get(0));
617 :
618 : // Steps 3-4 (reordered).
619 : // We use the reference to the reject function as a signal for whether
620 : // the resolve or reject function was already called, at which point
621 : // the references on each of the functions are cleared.
622 0 : if (!resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction).isObject()) {
623 0 : args.rval().setUndefined();
624 0 : return true;
625 : }
626 :
627 : // Steps 1-2 (reordered).
628 3006 : RootedObject promise(cx, &resolve->getExtendedSlot(ResolveFunctionSlot_Promise).toObject());
629 :
630 : // Step 5.
631 : // Here, we only remove the Promise reference from the resolution
632 : // functions. Actually marking it as fulfilled/rejected happens later.
633 1503 : ClearResolutionFunctionSlots(resolve);
634 :
635 : // In some cases the Promise reference on the resolution function won't
636 : // have been removed during resolution, so we need to check that here,
637 : // too.
638 0 : if (promise->is<PromiseObject>() &&
639 1469 : promise->as<PromiseObject>().state() != JS::PromiseState::Pending)
640 : {
641 : return true;
642 : }
643 :
644 : // Steps 6-13.
645 3006 : if (!ResolvePromiseInternal(cx, promise, resolutionVal))
646 : return false;
647 0 : args.rval().setUndefined();
648 1503 : return true;
649 : }
650 :
651 : static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
652 :
653 : /**
654 : * Tells the embedding to enqueue a Promise reaction job, based on
655 : * three parameters:
656 : * reactionObj - The reaction record.
657 : * handlerArg_ - The first and only argument to pass to the handler invoked by
658 : * the job. This will be stored on the reaction record.
659 : * targetState - The PromiseState this reaction job targets. This decides
660 : * whether the onFulfilled or onRejected handler is called.
661 : */
662 : MOZ_MUST_USE static bool
663 2916 : EnqueuePromiseReactionJob(JSContext* cx, HandleObject reactionObj,
664 : HandleValue handlerArg_, JS::PromiseState targetState)
665 : {
666 : // The reaction might have been stored on a Promise from another
667 : // compartment, which means it would've been wrapped in a CCW.
668 : // To properly handle that case here, unwrap it and enter its
669 : // compartment, where the job creation should take place anyway.
670 0 : Rooted<PromiseReactionRecord*> reaction(cx);
671 0 : RootedValue handlerArg(cx, handlerArg_);
672 0 : mozilla::Maybe<AutoRealm> ar;
673 0 : if (!IsProxy(reactionObj)) {
674 0 : MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
675 5826 : reaction = &reactionObj->as<PromiseReactionRecord>();
676 : } else {
677 0 : if (JS_IsDeadWrapper(UncheckedUnwrap(reactionObj))) {
678 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
679 0 : return false;
680 : }
681 0 : reaction = &UncheckedUnwrap(reactionObj)->as<PromiseReactionRecord>();
682 0 : MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>());
683 0 : ar.emplace(cx, reaction);
684 9 : if (!reaction->compartment()->wrap(cx, &handlerArg))
685 : return false;
686 : }
687 :
688 : // Must not enqueue a reaction job more than once.
689 2916 : MOZ_ASSERT(reaction->targetState() == JS::PromiseState::Pending);
690 :
691 0 : assertSameCompartment(cx, handlerArg);
692 5832 : reaction->setHandlerArg(handlerArg.get());
693 :
694 8748 : RootedValue reactionVal(cx, ObjectValue(*reaction));
695 :
696 0 : reaction->setTargetState(targetState);
697 5832 : RootedValue handler(cx, reaction->handler());
698 :
699 : // If we have a handler callback, we enter that handler's compartment so
700 : // that the promise reaction job function is created in that compartment.
701 : // That guarantees that the embedding ends up with the right entry global.
702 : // This is relevant for some html APIs like fetch that derive information
703 : // from said global.
704 0 : mozilla::Maybe<AutoRealm> ar2;
705 0 : if (handler.isObject()) {
706 3390 : RootedObject handlerObj(cx, &handler.toObject());
707 :
708 : // The unwrapping has to be unchecked because we specifically want to
709 : // be able to use handlers with wrappers that would only allow calls.
710 : // E.g., it's ok to have a handler from a chrome compartment in a
711 : // reaction to a content compartment's Promise instance.
712 0 : handlerObj = UncheckedUnwrap(handlerObj);
713 0 : MOZ_ASSERT(handlerObj);
714 1695 : ar2.emplace(cx, handlerObj);
715 :
716 : // We need to wrap the reaction to store it on the job function.
717 1 : if (!cx->compartment()->wrap(cx, &reactionVal))
718 0 : return false;
719 : }
720 :
721 : // Create the JS function to call when the job is triggered.
722 0 : RootedAtom funName(cx, cx->names().empty);
723 0 : RootedFunction job(cx, NewNativeFunction(cx, PromiseReactionJob, 0, funName,
724 0 : gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
725 2916 : if (!job)
726 : return false;
727 :
728 : // Store the reaction on the reaction job.
729 5832 : job->setExtendedSlot(ReactionJobSlot_ReactionRecord, reactionVal);
730 :
731 : // When using JS::AddPromiseReactions, no actual promise is created, so we
732 : // might not have one here.
733 : // Additionally, we might have an object here that isn't an instance of
734 : // Promise. This can happen if content overrides the value of
735 : // Promise[@@species] (or invokes Promise#then on a Promise subclass
736 : // instance with a non-default @@species value on the constructor) with a
737 : // function that returns objects that're not Promise (subclass) instances.
738 : // In that case, we just pretend we didn't have an object in the first
739 : // place.
740 : // If after all this we do have an object, wrap it in case we entered the
741 : // handler's compartment above, because we should pass objects from a
742 : // single compartment to the enqueuePromiseJob callback.
743 0 : RootedObject promise(cx, reaction->promise());
744 0 : if (promise && promise->is<PromiseObject>()) {
745 5634 : if (!cx->compartment()->wrap(cx, &promise))
746 : return false;
747 : }
748 :
749 : // Using objectFromIncumbentGlobal, we can derive the incumbent global by
750 : // unwrapping and then getting the global. This is very convoluted, but
751 : // much better than having to store the original global as a private value
752 : // because we couldn't wrap it to store it as a normal JS value.
753 0 : RootedObject global(cx);
754 0 : RootedObject objectFromIncumbentGlobal(cx, reaction->incumbentGlobalObject());
755 0 : if (objectFromIncumbentGlobal) {
756 0 : objectFromIncumbentGlobal = CheckedUnwrap(objectFromIncumbentGlobal);
757 0 : MOZ_ASSERT(objectFromIncumbentGlobal);
758 8748 : global = &objectFromIncumbentGlobal->nonCCWGlobal();
759 : }
760 :
761 : // Note: the global we pass here might be from a different compartment
762 : // than job and promise. While it's somewhat unusual to pass objects
763 : // from multiple compartments, in this case we specifically need the
764 : // global to be unwrapped because wrapping and unwrapping aren't
765 : // necessarily symmetric for globals.
766 11664 : return cx->runtime()->enqueuePromiseJob(cx, job, promise, global);
767 : }
768 :
769 : static MOZ_MUST_USE bool TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal,
770 : JS::PromiseState state, HandleValue valueOrReason);
771 :
772 : // ES2016, Commoned-out implementation of 25.4.1.4. and 25.4.1.7.
773 : static MOZ_MUST_USE bool
774 4103 : ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueOrReason,
775 : JS::PromiseState state)
776 : {
777 : // Step 1.
778 0 : MOZ_ASSERT(promise->state() == JS::PromiseState::Pending);
779 4103 : MOZ_ASSERT(state == JS::PromiseState::Fulfilled || state == JS::PromiseState::Rejected);
780 :
781 : // Step 2.
782 : // We only have one list of reactions for both resolution types. So
783 : // instead of getting the right list of reactions, we determine the
784 : // resolution type to retrieve the right information from the
785 : // reaction records.
786 8206 : RootedValue reactionsVal(cx, promise->getFixedSlot(PromiseSlot_ReactionsOrResult));
787 :
788 : // Steps 3-5.
789 : // The same slot is used for the reactions list and the result, so setting
790 : // the result also removes the reactions list.
791 8206 : promise->setFixedSlot(PromiseSlot_ReactionsOrResult, valueOrReason);
792 :
793 : // Step 6.
794 0 : int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
795 0 : flags |= PROMISE_FLAG_RESOLVED;
796 0 : if (state == JS::PromiseState::Fulfilled)
797 0 : flags |= PROMISE_FLAG_FULFILLED;
798 8206 : promise->setFixedSlot(PromiseSlot_Flags, Int32Value(flags));
799 :
800 : // Also null out the resolve/reject functions so they can be GC'd.
801 8206 : promise->setFixedSlot(PromiseSlot_RejectFunction, UndefinedValue());
802 :
803 : // Now that everything else is done, do the things the debugger needs.
804 : // Step 7 of RejectPromise implemented in onSettled.
805 4103 : PromiseObject::onSettled(cx, promise);
806 :
807 : // Step 7 of FulfillPromise.
808 : // Step 8 of RejectPromise.
809 0 : if (reactionsVal.isObject())
810 2303 : return TriggerPromiseReactions(cx, reactionsVal, state, valueOrReason);
811 :
812 : return true;
813 : }
814 :
815 : // ES2016, 25.4.1.4.
816 : static MOZ_MUST_USE bool
817 3991 : FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue value_)
818 : {
819 0 : Rooted<PromiseObject*> promise(cx);
820 7982 : RootedValue value(cx, value_);
821 :
822 0 : mozilla::Maybe<AutoRealm> ar;
823 0 : if (!IsProxy(promiseObj)) {
824 7916 : promise = &promiseObj->as<PromiseObject>();
825 : } else {
826 0 : if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) {
827 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
828 0 : return false;
829 : }
830 0 : promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>();
831 0 : ar.emplace(cx, promise);
832 99 : if (!promise->compartment()->wrap(cx, &value))
833 : return false;
834 : }
835 :
836 3991 : MOZ_ASSERT(promise->state() == JS::PromiseState::Pending);
837 :
838 7982 : return ResolvePromise(cx, promise, value, JS::PromiseState::Fulfilled);
839 : }
840 :
841 : static bool GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp);
842 : static bool PromiseConstructor(JSContext* cx, unsigned argc, Value* vp);
843 : static MOZ_MUST_USE PromiseObject* CreatePromiseObjectInternal(JSContext* cx,
844 : HandleObject proto = nullptr,
845 : bool protoIsWrapped = false,
846 : bool informDebugger = true);
847 :
848 : enum GetCapabilitiesExecutorSlots {
849 : GetCapabilitiesExecutorSlots_Resolve,
850 : GetCapabilitiesExecutorSlots_Reject
851 : };
852 :
853 : static MOZ_MUST_USE PromiseObject*
854 3952 : CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx)
855 : {
856 0 : PromiseObject* promise = CreatePromiseObjectInternal(cx);
857 3952 : if (!promise)
858 : return nullptr;
859 :
860 : AddPromiseFlags(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION |
861 0 : PROMISE_FLAG_DEFAULT_REJECT_FUNCTION);
862 3952 : return promise;
863 : }
864 :
865 : static MOZ_MUST_USE PromiseObject*
866 93 : CreatePromiseWithDefaultResolutionFunctions(JSContext* cx, MutableHandleObject resolve,
867 : MutableHandleObject reject)
868 : {
869 : // ES2016, 25.4.3.1., as if called with GetCapabilitiesExecutor as the
870 : // executor argument.
871 :
872 : // Steps 1-2 (Not applicable).
873 :
874 : // Steps 3-7.
875 0 : Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
876 93 : if (!promise)
877 : return nullptr;
878 :
879 : // Step 8.
880 93 : if (!CreateResolvingFunctions(cx, promise, resolve, reject))
881 : return nullptr;
882 :
883 279 : promise->setFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*reject));
884 :
885 : // Steps 9-10 (Not applicable).
886 :
887 : // Step 11.
888 93 : return promise;
889 : }
890 :
891 : // ES2016, 25.4.1.5.
892 : static MOZ_MUST_USE bool
893 1933 : NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise,
894 : MutableHandleObject resolve, MutableHandleObject reject,
895 : bool canOmitResolutionFunctions)
896 : {
897 5799 : RootedValue cVal(cx, ObjectValue(*C));
898 :
899 : // Steps 1-2.
900 0 : if (!IsConstructor(C)) {
901 0 : ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_SEARCH_STACK, cVal, nullptr);
902 0 : return false;
903 : }
904 :
905 : // If we'd call the original Promise constructor and know that the
906 : // resolve/reject functions won't ever escape to content, we can skip
907 : // creating and calling the executor function and instead return a Promise
908 : // marked as having default resolve/reject functions.
909 : //
910 : // This can't be used in Promise.all and Promise.race because we have to
911 : // pass the reject (and resolve, in the race case) function to thenables
912 : // in the list passed to all/race, which (potentially) means exposing them
913 : // to content.
914 : //
915 : // For Promise.all and Promise.race we can only optimize away the creation
916 : // of the GetCapabilitiesExecutor function, and directly allocate the
917 : // result promise instead of invoking the Promise constructor.
918 0 : if (IsNativeFunction(cVal, PromiseConstructor)) {
919 0 : if (canOmitResolutionFunctions)
920 1835 : promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx));
921 : else
922 0 : promise.set(CreatePromiseWithDefaultResolutionFunctions(cx, resolve, reject));
923 1928 : if (!promise)
924 : return false;
925 1928 : return true;
926 : }
927 :
928 : // Step 3 (omitted).
929 :
930 : // Step 4.
931 0 : RootedAtom funName(cx, cx->names().empty);
932 0 : RootedFunction executor(cx, NewNativeFunction(cx, GetCapabilitiesExecutor, 2, funName,
933 0 : gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
934 5 : if (!executor)
935 : return false;
936 :
937 : // Step 5 (omitted).
938 :
939 : // Step 6.
940 0 : FixedConstructArgs<1> cargs(cx);
941 0 : cargs[0].setObject(*executor);
942 10 : if (!Construct(cx, cVal, cargs, cVal, promise))
943 : return false;
944 :
945 : // Step 7.
946 0 : RootedValue resolveVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve));
947 5 : if (!IsCallable(resolveVal)) {
948 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
949 0 : JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE);
950 0 : return false;
951 : }
952 :
953 : // Step 8.
954 0 : RootedValue rejectVal(cx, executor->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject));
955 5 : if (!IsCallable(rejectVal)) {
956 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
957 0 : JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE);
958 0 : return false;
959 : }
960 :
961 : // Step 9 (well, the equivalent for all of promiseCapabilities' fields.)
962 0 : resolve.set(&resolveVal.toObject());
963 5 : reject.set(&rejectVal.toObject());
964 :
965 : // Step 10.
966 5 : return true;
967 : }
968 :
969 : // ES2016, 25.4.1.5.1.
970 : static bool
971 5 : GetCapabilitiesExecutor(JSContext* cx, unsigned argc, Value* vp)
972 : {
973 0 : CallArgs args = CallArgsFromVp(argc, vp);
974 10 : RootedFunction F(cx, &args.callee().as<JSFunction>());
975 :
976 : // Steps 1-2 (implicit).
977 :
978 : // Steps 3-4.
979 0 : if (!F->getExtendedSlot(GetCapabilitiesExecutorSlots_Resolve).isUndefined() ||
980 10 : !F->getExtendedSlot(GetCapabilitiesExecutorSlots_Reject).isUndefined())
981 : {
982 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
983 0 : JSMSG_PROMISE_CAPABILITY_HAS_SOMETHING_ALREADY);
984 0 : return false;
985 : }
986 :
987 : // Step 5.
988 15 : F->setExtendedSlot(GetCapabilitiesExecutorSlots_Resolve, args.get(0));
989 :
990 : // Step 6.
991 15 : F->setExtendedSlot(GetCapabilitiesExecutorSlots_Reject, args.get(1));
992 :
993 : // Step 7.
994 0 : args.rval().setUndefined();
995 5 : return true;
996 : }
997 :
998 : // ES2016, 25.4.1.7.
999 : static MOZ_MUST_USE bool
1000 112 : RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue reason_)
1001 : {
1002 0 : Rooted<PromiseObject*> promise(cx);
1003 224 : RootedValue reason(cx, reason_);
1004 :
1005 0 : mozilla::Maybe<AutoRealm> ar;
1006 0 : if (!IsProxy(promiseObj)) {
1007 224 : promise = &promiseObj->as<PromiseObject>();
1008 : } else {
1009 0 : if (JS_IsDeadWrapper(UncheckedUnwrap(promiseObj))) {
1010 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
1011 0 : return false;
1012 : }
1013 0 : promise = &UncheckedUnwrap(promiseObj)->as<PromiseObject>();
1014 0 : ar.emplace(cx, promise);
1015 :
1016 : // The rejection reason might've been created in a compartment with higher
1017 : // privileges than the Promise's. In that case, object-type rejection
1018 : // values might be wrapped into a wrapper that throws whenever the
1019 : // Promise's reaction handler wants to do anything useful with it. To
1020 : // avoid that situation, we synthesize a generic error that doesn't
1021 : // expose any privileged information but can safely be used in the
1022 : // rejection handler.
1023 0 : if (!promise->compartment()->wrap(cx, &reason))
1024 : return false;
1025 0 : if (reason.isObject() && !CheckedUnwrap(&reason.toObject())) {
1026 : // Report the existing reason, so we don't just drop it on the
1027 : // floor.
1028 0 : RootedObject realReason(cx, UncheckedUnwrap(&reason.toObject()));
1029 0 : RootedValue realReasonVal(cx, ObjectValue(*realReason));
1030 0 : RootedObject realGlobal(cx, &realReason->nonCCWGlobal());
1031 0 : ReportErrorToGlobal(cx, realGlobal, realReasonVal);
1032 :
1033 : // Async stacks are only properly adopted if there's at least one
1034 : // interpreter frame active right now. If a thenable job with a
1035 : // throwing `then` function got us here, that'll not be the case,
1036 : // so we add one by throwing the error from self-hosted code.
1037 0 : if (!GetInternalError(cx, JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON, &reason))
1038 0 : return false;
1039 : }
1040 : }
1041 :
1042 112 : MOZ_ASSERT(promise->state() == JS::PromiseState::Pending);
1043 :
1044 224 : return ResolvePromise(cx, promise, reason, JS::PromiseState::Rejected);
1045 : }
1046 :
1047 : // ES2016, 25.4.1.8.
1048 : static MOZ_MUST_USE bool
1049 2303 : TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseState state,
1050 : HandleValue valueOrReason)
1051 : {
1052 0 : RootedObject reactions(cx, &reactionsVal.toObject());
1053 4606 : RootedObject reaction(cx);
1054 :
1055 0 : if (reactions->is<PromiseReactionRecord>() ||
1056 0 : IsWrapper(reactions) ||
1057 100 : JS_IsDeadWrapper(reactions))
1058 : {
1059 2203 : return EnqueuePromiseReactionJob(cx, reactions, valueOrReason, state);
1060 : }
1061 :
1062 0 : RootedNativeObject reactionsList(cx, &reactions->as<NativeObject>());
1063 0 : size_t reactionsCount = reactionsList->getDenseInitializedLength();
1064 100 : MOZ_ASSERT(reactionsCount > 1, "Reactions list should be created lazily");
1065 :
1066 0 : RootedValue reactionVal(cx);
1067 0 : for (size_t i = 0; i < reactionsCount; i++) {
1068 0 : reactionVal = reactionsList->getDenseElement(i);
1069 0 : MOZ_RELEASE_ASSERT(reactionVal.isObject());
1070 0 : reaction = &reactionVal.toObject();
1071 201 : if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state))
1072 : return false;
1073 : }
1074 :
1075 : return true;
1076 : }
1077 :
1078 : static MOZ_MUST_USE bool
1079 1034 : AsyncFunctionPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
1080 : MutableHandleValue rval)
1081 : {
1082 1034 : MOZ_ASSERT(reaction->isAsyncFunction());
1083 :
1084 0 : RootedValue handlerVal(cx, reaction->handler());
1085 0 : RootedValue argument(cx, reaction->handlerArg());
1086 0 : Rooted<PromiseObject*> resultPromise(cx, &reaction->promise()->as<PromiseObject>());
1087 2068 : RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator));
1088 :
1089 1034 : int32_t handlerNum = int32_t(handlerVal.toNumber());
1090 :
1091 : // Await's handlers don't return a value, nor throw exception.
1092 : // They fail only on OOM.
1093 0 : if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
1094 3006 : if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument))
1095 : return false;
1096 : } else {
1097 0 : MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected);
1098 96 : if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument))
1099 : return false;
1100 : }
1101 :
1102 0 : rval.setUndefined();
1103 1034 : return true;
1104 : }
1105 :
1106 : static MOZ_MUST_USE bool
1107 0 : AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
1108 : MutableHandleValue rval)
1109 : {
1110 0 : MOZ_ASSERT(reaction->isAsyncGenerator());
1111 :
1112 0 : RootedValue handlerVal(cx, reaction->handler());
1113 0 : RootedValue argument(cx, reaction->handlerArg());
1114 0 : Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator());
1115 :
1116 0 : int32_t handlerNum = int32_t(handlerVal.toNumber());
1117 :
1118 : // Await's handlers don't return a value, nor throw exception.
1119 : // They fail only on OOM.
1120 0 : if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) {
1121 : // 4.1.1.
1122 0 : if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument))
1123 : return false;
1124 0 : } else if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedRejected) {
1125 : // 4.1.2.
1126 0 : if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument))
1127 : return false;
1128 0 : } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled) {
1129 0 : asyncGenObj->setCompleted();
1130 : // 11.4.3.5.1 step 1.
1131 0 : if (!AsyncGeneratorResolve(cx, asyncGenObj, argument, true))
1132 : return false;
1133 0 : } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnRejected) {
1134 0 : asyncGenObj->setCompleted();
1135 : // 11.4.3.5.2 step 1.
1136 0 : if (!AsyncGeneratorReject(cx, asyncGenObj, argument))
1137 : return false;
1138 0 : } else if (handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled) {
1139 0 : asyncGenObj->setExecuting();
1140 : // 11.4.3.7 steps 8.d-e.
1141 0 : if (!AsyncGeneratorYieldReturnAwaitedFulfilled(cx, asyncGenObj, argument))
1142 : return false;
1143 : } else {
1144 0 : MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected);
1145 0 : asyncGenObj->setExecuting();
1146 : // 11.4.3.7 step 8.c.
1147 0 : if (!AsyncGeneratorYieldReturnAwaitedRejected(cx, asyncGenObj, argument))
1148 : return false;
1149 : }
1150 :
1151 0 : rval.setUndefined();
1152 0 : return true;
1153 : }
1154 :
1155 : // ES2016, 25.4.2.1.
1156 : /**
1157 : * Callback triggering the fulfill/reject reaction for a resolved Promise,
1158 : * to be invoked by the embedding during its processing of the Promise job
1159 : * queue.
1160 : *
1161 : * See http://www.ecma-international.org/ecma-262/7.0/index.html#sec-jobs-and-job-queues
1162 : *
1163 : * A PromiseReactionJob is set as the native function of an extended
1164 : * JSFunction object, with all information required for the job's
1165 : * execution stored in in a reaction record in its first extended slot.
1166 : */
1167 : static bool
1168 2916 : PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
1169 : {
1170 2916 : CallArgs args = CallArgsFromVp(argc, vp);
1171 :
1172 5832 : RootedFunction job(cx, &args.callee().as<JSFunction>());
1173 :
1174 5832 : RootedObject reactionObj(cx, &job->getExtendedSlot(ReactionJobSlot_ReactionRecord).toObject());
1175 :
1176 : // To ensure that the embedding ends up with the right entry global, we're
1177 : // guaranteeing that the reaction job function gets created in the same
1178 : // compartment as the handler function. That's not necessarily the global
1179 : // that the job was triggered from, though.
1180 : // We can find the triggering global via the job's reaction record. To go
1181 : // back, we check if the reaction is a wrapper and if so, unwrap it and
1182 : // enter its compartment.
1183 0 : mozilla::Maybe<AutoRealm> ar;
1184 0 : if (!IsProxy(reactionObj)) {
1185 5824 : MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
1186 : } else {
1187 0 : reactionObj = UncheckedUnwrap(reactionObj);
1188 1 : if (JS_IsDeadWrapper(reactionObj)) {
1189 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
1190 0 : return false;
1191 : }
1192 0 : MOZ_RELEASE_ASSERT(reactionObj->is<PromiseReactionRecord>());
1193 4 : ar.emplace(cx, reactionObj);
1194 : }
1195 :
1196 : // Steps 1-2.
1197 0 : Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
1198 0 : if (reaction->isAsyncFunction())
1199 0 : return AsyncFunctionPromiseReactionJob(cx, reaction, args.rval());
1200 0 : if (reaction->isAsyncGenerator())
1201 0 : return AsyncGeneratorPromiseReactionJob(cx, reaction, args.rval());
1202 :
1203 : // Step 3.
1204 3764 : RootedValue handlerVal(cx, reaction->handler());
1205 :
1206 3764 : RootedValue argument(cx, reaction->handlerArg());
1207 :
1208 0 : RootedValue handlerResult(cx);
1209 1882 : ResolutionMode resolutionMode = ResolveMode;
1210 :
1211 : // Steps 4-6.
1212 0 : if (handlerVal.isNumber()) {
1213 187 : int32_t handlerNum = int32_t(handlerVal.toNumber());
1214 :
1215 : // Step 4.
1216 187 : if (handlerNum == PromiseHandlerIdentity) {
1217 : handlerResult = argument;
1218 26 : } else if (handlerNum == PromiseHandlerThrower) {
1219 : // Step 5.
1220 26 : resolutionMode = RejectMode;
1221 : handlerResult = argument;
1222 : } else {
1223 0 : MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone ||
1224 : handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone);
1225 :
1226 0 : bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone;
1227 : // Async Iteration proposal 11.1.3.2.5 step 1.
1228 0 : RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done));
1229 0 : if (!resultObj)
1230 0 : return false;
1231 :
1232 0 : handlerResult = ObjectValue(*resultObj);
1233 : }
1234 : } else {
1235 : // Step 6.
1236 0 : FixedInvokeArgs<1> args2(cx);
1237 0 : args2[0].set(argument);
1238 0 : if (!Call(cx, handlerVal, UndefinedHandleValue, args2, &handlerResult)) {
1239 0 : resolutionMode = RejectMode;
1240 0 : if (!MaybeGetAndClearException(cx, &handlerResult))
1241 0 : return false;
1242 : }
1243 : }
1244 :
1245 : // Steps 7-9.
1246 : size_t hookSlot = resolutionMode == RejectMode
1247 0 : ? ReactionRecordSlot_Reject
1248 0 : : ReactionRecordSlot_Resolve;
1249 0 : RootedObject callee(cx, reaction->getFixedSlot(hookSlot).toObjectOrNull());
1250 0 : RootedObject promiseObj(cx, reaction->promise());
1251 5646 : if (!RunResolutionFunction(cx, callee, handlerResult, resolutionMode, promiseObj))
1252 : return false;
1253 :
1254 0 : args.rval().setUndefined();
1255 1882 : return true;
1256 : }
1257 :
1258 : // ES2016, 25.4.2.2.
1259 : /**
1260 : * Callback for resolving a thenable, to be invoked by the embedding during
1261 : * its processing of the Promise job queue.
1262 : *
1263 : * See http://www.ecma-international.org/ecma-262/7.0/index.html#sec-jobs-and-job-queues
1264 : *
1265 : * A PromiseResolveThenableJob is set as the native function of an extended
1266 : * JSFunction object, with all information required for the job's
1267 : * execution stored in the function's extended slots.
1268 : *
1269 : * Usage of the function's extended slots is as follows:
1270 : * ThenableJobSlot_Handler: The handler to use as the Promise reaction.
1271 : * This can be PromiseHandlerIdentity,
1272 : * PromiseHandlerThrower, or a callable. In the
1273 : * latter case, it's guaranteed to be an object
1274 : * from the same compartment as the
1275 : * PromiseReactionJob.
1276 : * ThenableJobSlot_JobData: JobData - a, potentially CCW-wrapped, dense list
1277 : * containing data required for proper execution of
1278 : * the reaction.
1279 : *
1280 : * The JobData list has the following entries:
1281 : * ThenableJobDataSlot_Promise: The Promise to resolve using the given
1282 : * thenable.
1283 : * ThenableJobDataSlot_Thenable: The thenable to use as the receiver when
1284 : * calling the `then` function.
1285 : */
1286 : static bool
1287 1232 : PromiseResolveThenableJob(JSContext* cx, unsigned argc, Value* vp)
1288 : {
1289 1232 : CallArgs args = CallArgsFromVp(argc, vp);
1290 :
1291 0 : RootedFunction job(cx, &args.callee().as<JSFunction>());
1292 0 : RootedValue then(cx, job->getExtendedSlot(ThenableJobSlot_Handler));
1293 0 : MOZ_ASSERT(!IsWrapper(&then.toObject()));
1294 0 : RootedNativeObject jobArgs(cx, &job->getExtendedSlot(ThenableJobSlot_JobData)
1295 3696 : .toObject().as<NativeObject>());
1296 :
1297 0 : RootedObject promise(cx, &jobArgs->getDenseElement(ThenableJobDataIndex_Promise).toObject());
1298 2464 : RootedValue thenable(cx, jobArgs->getDenseElement(ThenableJobDataIndex_Thenable));
1299 :
1300 : // Step 1.
1301 0 : RootedObject resolveFn(cx);
1302 0 : RootedObject rejectFn(cx);
1303 3696 : if (!CreateResolvingFunctions(cx, promise, &resolveFn, &rejectFn))
1304 : return false;
1305 :
1306 : // Step 2.
1307 0 : FixedInvokeArgs<2> args2(cx);
1308 0 : args2[0].setObject(*resolveFn);
1309 2464 : args2[1].setObject(*rejectFn);
1310 :
1311 2464 : RootedValue rval(cx);
1312 :
1313 : // In difference to the usual pattern, we return immediately on success.
1314 3696 : if (Call(cx, then, thenable, args2, &rval))
1315 : return true;
1316 :
1317 0 : if (!MaybeGetAndClearException(cx, &rval))
1318 : return false;
1319 :
1320 0 : FixedInvokeArgs<1> rejectArgs(cx);
1321 0 : rejectArgs[0].set(rval);
1322 :
1323 0 : RootedValue rejectVal(cx, ObjectValue(*rejectFn));
1324 0 : return Call(cx, rejectVal, UndefinedHandleValue, rejectArgs, &rval);
1325 : }
1326 :
1327 : /**
1328 : * Tells the embedding to enqueue a Promise resolve thenable job, based on
1329 : * three parameters:
1330 : * promiseToResolve_ - The promise to resolve, obviously.
1331 : * thenable_ - The thenable to resolve the Promise with.
1332 : * thenVal - The `then` function to invoke with the `thenable` as the receiver.
1333 : */
1334 : static MOZ_MUST_USE bool
1335 1232 : EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue promiseToResolve_,
1336 : HandleValue thenable_, HandleValue thenVal)
1337 : {
1338 : // Need to re-root these to enable wrapping them below.
1339 0 : RootedValue promiseToResolve(cx, promiseToResolve_);
1340 2464 : RootedValue thenable(cx, thenable_);
1341 :
1342 : // We enter the `then` callable's compartment so that the job function is
1343 : // created in that compartment.
1344 : // That guarantees that the embedding ends up with the right entry global.
1345 : // This is relevant for some html APIs like fetch that derive information
1346 : // from said global.
1347 0 : RootedObject then(cx, CheckedUnwrap(&thenVal.toObject()));
1348 3696 : AutoRealm ar(cx, then);
1349 :
1350 0 : RootedAtom funName(cx, cx->names().empty);
1351 0 : RootedFunction job(cx, NewNativeFunction(cx, PromiseResolveThenableJob, 0, funName,
1352 0 : gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
1353 1232 : if (!job)
1354 : return false;
1355 :
1356 : // Store the `then` function on the callback.
1357 3696 : job->setExtendedSlot(ThenableJobSlot_Handler, ObjectValue(*then));
1358 :
1359 : // Create a dense array to hold the data needed for the reaction job to
1360 : // work.
1361 : // See the doc comment for PromiseResolveThenableJob for the layout.
1362 0 : RootedArrayObject data(cx, NewDenseFullyAllocatedArray(cx, ThenableJobDataLength));
1363 0 : if (!data ||
1364 2464 : data->ensureDenseElements(cx, 0, ThenableJobDataLength) != DenseElementResult::Success)
1365 : {
1366 : return false;
1367 : }
1368 :
1369 : // Wrap and set the `promiseToResolve` argument.
1370 2464 : if (!cx->compartment()->wrap(cx, &promiseToResolve))
1371 : return false;
1372 2464 : data->setDenseElement(ThenableJobDataIndex_Promise, promiseToResolve);
1373 : // At this point the promise is guaranteed to be wrapped into the job's
1374 : // compartment.
1375 2464 : RootedObject promise(cx, &promiseToResolve.toObject());
1376 :
1377 : // Wrap and set the `thenable` argument.
1378 0 : MOZ_ASSERT(thenable.isObject());
1379 2464 : if (!cx->compartment()->wrap(cx, &thenable))
1380 : return false;
1381 2464 : data->setDenseElement(ThenableJobDataIndex_Thenable, thenable);
1382 :
1383 : // Store the data array on the reaction job.
1384 3696 : job->setExtendedSlot(ThenableJobSlot_JobData, ObjectValue(*data));
1385 :
1386 0 : RootedObject incumbentGlobal(cx, cx->runtime()->getIncumbentGlobal(cx));
1387 4928 : return cx->runtime()->enqueuePromiseJob(cx, job, promise, incumbentGlobal);
1388 : }
1389 :
1390 : static MOZ_MUST_USE bool
1391 : AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled,
1392 : HandleValue onRejected, HandleObject dependentPromise,
1393 : HandleObject resolve, HandleObject reject, HandleObject incumbentGlobal);
1394 :
1395 : static MOZ_MUST_USE bool
1396 : AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise,
1397 : Handle<PromiseReactionRecord*> reaction);
1398 :
1399 : static MOZ_MUST_USE bool BlockOnPromise(JSContext* cx, HandleValue promise,
1400 : HandleObject blockedPromise,
1401 : HandleValue onFulfilled, HandleValue onRejected);
1402 :
1403 : static JSFunction*
1404 66 : GetResolveFunctionFromReject(JSFunction* reject)
1405 : {
1406 0 : MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction);
1407 0 : Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction);
1408 0 : MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction));
1409 66 : return &resolveFunVal.toObject().as<JSFunction>();
1410 : }
1411 :
1412 : static JSFunction*
1413 1503 : GetRejectFunctionFromResolve(JSFunction* resolve)
1414 : {
1415 0 : MOZ_ASSERT(resolve->maybeNative() == ResolvePromiseFunction);
1416 0 : Value rejectFunVal = resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction);
1417 0 : MOZ_ASSERT(IsNativeFunction(rejectFunVal, RejectPromiseFunction));
1418 1503 : return &rejectFunVal.toObject().as<JSFunction>();
1419 : }
1420 :
1421 : static JSFunction*
1422 0 : GetResolveFunctionFromPromise(PromiseObject* promise)
1423 : {
1424 0 : Value rejectFunVal = promise->getFixedSlot(PromiseSlot_RejectFunction);
1425 0 : if (rejectFunVal.isUndefined())
1426 : return nullptr;
1427 0 : JSObject* rejectFunObj = &rejectFunVal.toObject();
1428 :
1429 : // We can safely unwrap it because all we want is to get the resolve
1430 : // function.
1431 0 : if (IsWrapper(rejectFunObj))
1432 0 : rejectFunObj = UncheckedUnwrap(rejectFunObj);
1433 :
1434 0 : if (!rejectFunObj->is<JSFunction>())
1435 : return nullptr;
1436 :
1437 0 : JSFunction* rejectFun = &rejectFunObj->as<JSFunction>();
1438 :
1439 : // Only the original RejectPromiseFunction has a reference to the resolve
1440 : // function.
1441 0 : if (rejectFun->maybeNative() != &RejectPromiseFunction)
1442 : return nullptr;
1443 :
1444 0 : return GetResolveFunctionFromReject(rejectFun);
1445 : }
1446 :
1447 : static void
1448 1569 : ClearResolutionFunctionSlots(JSFunction* resolutionFun)
1449 : {
1450 : JSFunction* resolve;
1451 : JSFunction* reject;
1452 0 : if (resolutionFun->maybeNative() == ResolvePromiseFunction) {
1453 0 : resolve = resolutionFun;
1454 1503 : reject = GetRejectFunctionFromResolve(resolutionFun);
1455 : } else {
1456 0 : resolve = GetResolveFunctionFromReject(resolutionFun);
1457 66 : reject = resolutionFun;
1458 : }
1459 :
1460 0 : resolve->setExtendedSlot(ResolveFunctionSlot_Promise, UndefinedValue());
1461 1569 : resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, UndefinedValue());
1462 :
1463 0 : reject->setExtendedSlot(RejectFunctionSlot_Promise, UndefinedValue());
1464 0 : reject->setExtendedSlot(RejectFunctionSlot_ResolveFunction, UndefinedValue());
1465 1569 : }
1466 :
1467 : // ES2016, 25.4.3.1. steps 3-7.
1468 : static MOZ_MUST_USE MOZ_ALWAYS_INLINE PromiseObject*
1469 4410 : CreatePromiseObjectInternal(JSContext* cx, HandleObject proto /* = nullptr */,
1470 : bool protoIsWrapped /* = false */, bool informDebugger /* = true */)
1471 : {
1472 : // Step 3.
1473 : // Enter the unwrapped proto's compartment, if that's different from
1474 : // the current one.
1475 : // All state stored in a Promise's fixed slots must be created in the
1476 : // same compartment, so we get all of that out of the way here.
1477 : // (Except for the resolution functions, which are created below.)
1478 0 : mozilla::Maybe<AutoRealm> ar;
1479 0 : if (protoIsWrapped)
1480 8 : ar.emplace(cx, proto);
1481 :
1482 0 : PromiseObject* promise = NewObjectWithClassProto<PromiseObject>(cx, proto);
1483 4410 : if (!promise)
1484 : return nullptr;
1485 :
1486 : // Step 4.
1487 4410 : promise->initFixedSlot(PromiseSlot_Flags, Int32Value(0));
1488 :
1489 : // Steps 5-6.
1490 : // Omitted, we allocate our single list of reaction records lazily.
1491 :
1492 : // Step 7.
1493 : // Implicit, the handled flag is unset by default.
1494 :
1495 0 : if (MOZ_LIKELY(!ShouldCaptureDebugInfo(cx)))
1496 0 : return promise;
1497 :
1498 : // Store an allocation stack so we can later figure out what the
1499 : // control flow was for some unexpected results. Frightfully expensive,
1500 : // but oh well.
1501 :
1502 8820 : Rooted<PromiseObject*> promiseRoot(cx, promise);
1503 :
1504 0 : PromiseDebugInfo* debugInfo = PromiseDebugInfo::create(cx, promiseRoot);
1505 4410 : if (!debugInfo)
1506 : return nullptr;
1507 :
1508 : // Let the Debugger know about this Promise.
1509 0 : if (informDebugger)
1510 4045 : Debugger::onNewPromise(cx, promiseRoot);
1511 :
1512 4410 : return promiseRoot;
1513 : }
1514 :
1515 : // ES2016, 25.4.3.1.
1516 : static bool
1517 365 : PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
1518 : {
1519 365 : CallArgs args = CallArgsFromVp(argc, vp);
1520 :
1521 : // Step 1.
1522 365 : if (!ThrowIfNotConstructing(cx, args, "Promise"))
1523 : return false;
1524 :
1525 : // Step 2.
1526 0 : RootedValue executorVal(cx, args.get(0));
1527 0 : if (!IsCallable(executorVal))
1528 0 : return ReportIsNotFunction(cx, executorVal);
1529 730 : RootedObject executor(cx, &executorVal.toObject());
1530 :
1531 : // Steps 3-10.
1532 1095 : RootedObject newTarget(cx, &args.newTarget().toObject());
1533 :
1534 : // If the constructor is called via an Xray wrapper, then the newTarget
1535 : // hasn't been unwrapped. We want that because, while the actual instance
1536 : // should be created in the target compartment, the constructor's code
1537 : // should run in the wrapper's compartment.
1538 : //
1539 : // This is so that the resolve and reject callbacks get created in the
1540 : // wrapper's compartment, which is required for code in that compartment
1541 : // to freely interact with it, and, e.g., pass objects as arguments, which
1542 : // it wouldn't be able to if the callbacks were themselves wrapped in Xray
1543 : // wrappers.
1544 : //
1545 : // At the same time, just creating the Promise itself in the wrapper's
1546 : // compartment wouldn't be helpful: if the wrapper forbids interactions
1547 : // with objects except for specific actions, such as calling them, then
1548 : // the code we want to expose it to can't actually treat it as a Promise:
1549 : // calling .then on it would throw, for example.
1550 : //
1551 : // Another scenario where it's important to create the Promise in a
1552 : // different compartment from the resolution functions is when we want to
1553 : // give non-privileged code a Promise resolved with the result of a
1554 : // Promise from privileged code; as a return value of a JS-implemented
1555 : // API, say. If the resolution functions were unprivileged, then resolving
1556 : // with a privileged Promise would cause `resolve` to attempt accessing
1557 : // .then on the passed Promise, which would throw an exception, so we'd
1558 : // just end up with a rejected Promise. Really, we want to chain the two
1559 : // Promises, with the unprivileged one resolved with the resolution of the
1560 : // privileged one.
1561 :
1562 0 : bool needsWrapping = false;
1563 0 : RootedObject proto(cx);
1564 0 : if (IsWrapper(newTarget)) {
1565 0 : JSObject* unwrappedNewTarget = CheckedUnwrap(newTarget);
1566 0 : MOZ_ASSERT(unwrappedNewTarget);
1567 16 : MOZ_ASSERT(unwrappedNewTarget != newTarget);
1568 :
1569 16 : newTarget = unwrappedNewTarget;
1570 : {
1571 0 : AutoRealm ar(cx, newTarget);
1572 0 : Handle<GlobalObject*> global = cx->global();
1573 0 : RootedObject promiseCtor(cx, GlobalObject::getOrCreatePromiseConstructor(cx, global));
1574 1 : if (!promiseCtor)
1575 0 : return false;
1576 :
1577 : // Promise subclasses don't get the special Xray treatment, so
1578 : // we only need to do the complex wrapping and unwrapping scheme
1579 : // described above for instances of Promise itself.
1580 0 : if (newTarget == promiseCtor) {
1581 0 : needsWrapping = true;
1582 0 : proto = GlobalObject::getOrCreatePromisePrototype(cx, cx->global());
1583 8 : if (!proto)
1584 : return false;
1585 : }
1586 : }
1587 : }
1588 :
1589 0 : if (needsWrapping) {
1590 16 : if (!cx->compartment()->wrap(cx, &proto))
1591 : return false;
1592 : } else {
1593 357 : if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
1594 : return false;
1595 : }
1596 0 : PromiseObject* promise = PromiseObject::create(cx, executor, proto, needsWrapping);
1597 365 : if (!promise)
1598 : return false;
1599 :
1600 : // Step 11.
1601 0 : args.rval().setObject(*promise);
1602 0 : if (needsWrapping)
1603 16 : return cx->compartment()->wrap(cx, args.rval());
1604 : return true;
1605 : }
1606 :
1607 : // ES2016, 25.4.3.1. steps 3-11.
1608 : /* static */ PromiseObject*
1609 365 : PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto /* = nullptr */,
1610 : bool needsWrapping /* = false */)
1611 : {
1612 730 : MOZ_ASSERT(executor->isCallable());
1613 :
1614 730 : RootedObject usedProto(cx, proto);
1615 : // If the proto is wrapped, that means the current function is running
1616 : // with a different compartment active from the one the Promise instance
1617 : // is to be created in.
1618 : // See the comment in PromiseConstructor for details.
1619 0 : if (needsWrapping) {
1620 0 : MOZ_ASSERT(proto);
1621 0 : usedProto = CheckedUnwrap(proto);
1622 8 : if (!usedProto)
1623 : return nullptr;
1624 : }
1625 :
1626 :
1627 : // Steps 3-7.
1628 0 : Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, needsWrapping,
1629 0 : false));
1630 365 : if (!promise)
1631 : return nullptr;
1632 :
1633 0 : RootedObject promiseObj(cx, promise);
1634 381 : if (needsWrapping && !cx->compartment()->wrap(cx, &promiseObj))
1635 : return nullptr;
1636 :
1637 : // Step 8.
1638 : // The resolving functions are created in the compartment active when the
1639 : // (maybe wrapped) Promise constructor was called. They contain checks and
1640 : // can unwrap the Promise if required.
1641 0 : RootedObject resolveFn(cx);
1642 0 : RootedObject rejectFn(cx);
1643 1095 : if (!CreateResolvingFunctions(cx, promiseObj, &resolveFn, &rejectFn))
1644 : return nullptr;
1645 :
1646 : // Need to wrap the resolution functions before storing them on the Promise.
1647 730 : MOZ_ASSERT(promise->getFixedSlot(PromiseSlot_RejectFunction).isUndefined(),
1648 : "Slot must be undefined so initFixedSlot can be used");
1649 0 : if (needsWrapping) {
1650 0 : AutoRealm ar(cx, promise);
1651 0 : RootedObject wrappedRejectFn(cx, rejectFn);
1652 1 : if (!cx->compartment()->wrap(cx, &wrappedRejectFn))
1653 0 : return nullptr;
1654 24 : promise->initFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*wrappedRejectFn));
1655 : } else {
1656 1071 : promise->initFixedSlot(PromiseSlot_RejectFunction, ObjectValue(*rejectFn));
1657 : }
1658 :
1659 : // Step 9.
1660 : bool success;
1661 : {
1662 1095 : FixedInvokeArgs<2> args(cx);
1663 :
1664 0 : args[0].setObject(*resolveFn);
1665 730 : args[1].setObject(*rejectFn);
1666 :
1667 0 : RootedValue calleeOrRval(cx, ObjectValue(*executor));
1668 730 : success = Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval);
1669 : }
1670 :
1671 : // Step 10.
1672 0 : if (!success) {
1673 0 : RootedValue exceptionVal(cx);
1674 0 : if (!MaybeGetAndClearException(cx, &exceptionVal))
1675 0 : return nullptr;
1676 :
1677 0 : FixedInvokeArgs<1> args(cx);
1678 :
1679 0 : args[0].set(exceptionVal);
1680 :
1681 0 : RootedValue calleeOrRval(cx, ObjectValue(*rejectFn));
1682 0 : if (!Call(cx, calleeOrRval, UndefinedHandleValue, args, &calleeOrRval))
1683 0 : return nullptr;
1684 : }
1685 :
1686 : // Let the Debugger know about this Promise.
1687 730 : Debugger::onNewPromise(cx, promise);
1688 :
1689 : // Step 11.
1690 365 : return promise;
1691 : }
1692 :
1693 : // ES2016, 25.4.3.1. skipping creation of resolution functions and executor
1694 : // function invocation.
1695 : /* static */ PromiseObject*
1696 97 : PromiseObject::createSkippingExecutor(JSContext* cx)
1697 : {
1698 97 : return CreatePromiseObjectWithoutResolutionFunctions(cx);
1699 : }
1700 :
1701 : static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator,
1702 : HandleObject C, HandleObject promiseObj,
1703 : HandleObject resolve, HandleObject reject,
1704 : bool* done);
1705 :
1706 : // ES2016, 25.4.4.1.
1707 : static bool
1708 85 : Promise_static_all(JSContext* cx, unsigned argc, Value* vp)
1709 : {
1710 0 : CallArgs args = CallArgsFromVp(argc, vp);
1711 170 : RootedValue iterable(cx, args.get(0));
1712 :
1713 : // Step 2 (reordered).
1714 0 : RootedValue CVal(cx, args.thisv());
1715 85 : if (!CVal.isObject()) {
1716 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
1717 0 : "Receiver of Promise.all call");
1718 0 : return false;
1719 : }
1720 :
1721 : // Step 1.
1722 170 : RootedObject C(cx, &CVal.toObject());
1723 :
1724 : // Step 3.
1725 0 : RootedObject resultPromise(cx);
1726 0 : RootedObject resolve(cx);
1727 0 : RootedObject reject(cx);
1728 340 : if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, false))
1729 : return false;
1730 :
1731 : // Steps 4-5.
1732 0 : JS::ForOfIterator iter(cx);
1733 0 : if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable))
1734 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
1735 :
1736 85 : if (!iter.valueIsIterable()) {
1737 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
1738 0 : "Argument of Promise.all");
1739 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
1740 : }
1741 :
1742 : // Step 6 (implicit).
1743 :
1744 : // Step 7.
1745 : bool done;
1746 340 : bool result = PerformPromiseAll(cx, iter, C, resultPromise, resolve, reject, &done);
1747 :
1748 : // Step 8.
1749 85 : if (!result) {
1750 : // Step 8.a.
1751 0 : if (!done)
1752 0 : iter.closeThrow();
1753 :
1754 : // Step 8.b.
1755 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
1756 : }
1757 :
1758 : // Step 9.
1759 0 : args.rval().setObject(*resultPromise);
1760 85 : return true;
1761 : }
1762 :
1763 : static MOZ_MUST_USE bool PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
1764 : HandleValue onFulfilled_, HandleValue onRejected_,
1765 : HandleObject resultPromise,
1766 : HandleObject resolve, HandleObject reject);
1767 :
1768 : static bool PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp);
1769 :
1770 : // Unforgeable version of ES2016, 25.4.4.1.
1771 : MOZ_MUST_USE JSObject*
1772 0 : js::GetWaitForAllPromise(JSContext* cx, const JS::AutoObjectVector& promises)
1773 : {
1774 : #ifdef DEBUG
1775 0 : for (size_t i = 0, len = promises.length(); i < len; i++) {
1776 0 : JSObject* obj = promises[i];
1777 0 : assertSameCompartment(cx, obj);
1778 0 : MOZ_ASSERT(UncheckedUnwrap(obj)->is<PromiseObject>());
1779 : }
1780 : #endif
1781 :
1782 : // Step 1.
1783 0 : RootedObject C(cx, GlobalObject::getOrCreatePromiseConstructor(cx, cx->global()));
1784 0 : if (!C)
1785 : return nullptr;
1786 :
1787 : // Step 2 (omitted).
1788 :
1789 : // Step 3.
1790 0 : RootedObject resultPromise(cx);
1791 0 : RootedObject resolve(cx);
1792 0 : RootedObject reject(cx);
1793 0 : if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, false))
1794 : return nullptr;
1795 :
1796 : // Steps 4-6 (omitted).
1797 :
1798 : // Step 7.
1799 : // Implemented as an inlined, simplied version of ES2016 25.4.4.1.1, PerformPromiseAll.
1800 : {
1801 0 : uint32_t promiseCount = promises.length();
1802 : // Sub-steps 1-2 (omitted).
1803 :
1804 : // Sub-step 3.
1805 0 : RootedNativeObject valuesArray(cx, NewDenseFullyAllocatedArray(cx, promiseCount));
1806 0 : if (!valuesArray)
1807 0 : return nullptr;
1808 0 : if (valuesArray->ensureDenseElements(cx, 0, promiseCount) != DenseElementResult::Success)
1809 : return nullptr;
1810 :
1811 : // Sub-step 4.
1812 : // Create our data holder that holds all the things shared across
1813 : // every step of the iterator. In particular, this holds the
1814 : // remainingElementsCount (as an integer reserved slot), the array of
1815 : // values, and the resolve function from our PromiseCapability.
1816 0 : RootedValue valuesArrayVal(cx, ObjectValue(*valuesArray));
1817 0 : Rooted<PromiseAllDataHolder*> dataHolder(cx, NewPromiseAllDataHolder(cx, resultPromise,
1818 : valuesArrayVal,
1819 0 : resolve));
1820 0 : if (!dataHolder)
1821 0 : return nullptr;
1822 0 : RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
1823 :
1824 : // Sub-step 5 (inline in loop-header below).
1825 :
1826 : // Sub-step 6.
1827 0 : for (uint32_t index = 0; index < promiseCount; index++) {
1828 : // Steps a-c (omitted).
1829 : // Step d (implemented after the loop).
1830 : // Steps e-g (omitted).
1831 :
1832 : // Step h.
1833 0 : valuesArray->setDenseElement(index, UndefinedHandleValue);
1834 :
1835 : // Step i, vastly simplified.
1836 0 : RootedObject nextPromiseObj(cx, promises[index]);
1837 :
1838 : // Step j.
1839 0 : RootedFunction resolveFunc(cx, NewNativeFunction(cx, PromiseAllResolveElementFunction,
1840 : 1, nullptr,
1841 : gc::AllocKind::FUNCTION_EXTENDED,
1842 0 : GenericObject));
1843 0 : if (!resolveFunc)
1844 0 : return nullptr;
1845 :
1846 : // Steps k-o.
1847 0 : resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal);
1848 0 : resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
1849 0 : Int32Value(index));
1850 :
1851 : // Step p.
1852 0 : dataHolder->increaseRemainingCount();
1853 :
1854 : // Step q, very roughly.
1855 0 : RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc));
1856 0 : RootedValue rejectFunVal(cx, ObjectValue(*reject));
1857 0 : Rooted<PromiseObject*> nextPromise(cx);
1858 :
1859 : // GetWaitForAllPromise is used internally only and must not
1860 : // trigger content-observable effects when registering a reaction.
1861 : // It's also meant to work on wrapped Promises, potentially from
1862 : // compartments with principals inaccessible from the current
1863 : // compartment. To make that work, it unwraps promises with
1864 : // UncheckedUnwrap,
1865 0 : nextPromise = &UncheckedUnwrap(nextPromiseObj)->as<PromiseObject>();
1866 :
1867 0 : if (!PerformPromiseThen(cx, nextPromise, resolveFunVal, rejectFunVal,
1868 : resultPromise, nullptr, nullptr))
1869 : {
1870 0 : return nullptr;
1871 : }
1872 :
1873 : // Step r (inline in loop-header).
1874 : }
1875 :
1876 : // Sub-step d.i (implicit).
1877 : // Sub-step d.ii.
1878 0 : int32_t remainingCount = dataHolder->decreaseRemainingCount();
1879 :
1880 : // Sub-step d.iii-iv.
1881 0 : if (remainingCount == 0) {
1882 0 : RootedValue valuesArrayVal(cx, ObjectValue(*valuesArray));
1883 0 : if (!ResolvePromiseInternal(cx, resultPromise, valuesArrayVal))
1884 0 : return nullptr;
1885 : }
1886 : }
1887 :
1888 : // Step 8 (omitted).
1889 :
1890 : // Step 9.
1891 0 : return resultPromise;
1892 : }
1893 :
1894 : static MOZ_MUST_USE bool
1895 2103 : RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue result,
1896 : ResolutionMode mode, HandleObject promiseObj)
1897 : {
1898 : // The absence of a resolve/reject function can mean that, as an
1899 : // optimization, those weren't created. In that case, a flag is set on
1900 : // the Promise object. (It's also possible to not have a resolution
1901 : // function without that flag being set. This can occur if a Promise
1902 : // subclass constructor passes null/undefined to `super()`.)
1903 : // There are also reactions where the Promise itself is missing. For
1904 : // those, there's nothing left to do here.
1905 0 : assertSameCompartment(cx, resolutionFun);
1906 0 : assertSameCompartment(cx, result);
1907 0 : assertSameCompartment(cx, promiseObj);
1908 0 : if (resolutionFun) {
1909 0 : RootedValue calleeOrRval(cx, ObjectValue(*resolutionFun));
1910 0 : FixedInvokeArgs<1> resolveArgs(cx);
1911 0 : resolveArgs[0].set(result);
1912 178 : return Call(cx, calleeOrRval, UndefinedHandleValue, resolveArgs, &calleeOrRval);
1913 : }
1914 :
1915 2014 : if (!promiseObj)
1916 : return true;
1917 :
1918 0 : Rooted<PromiseObject*> promise(cx, &promiseObj->as<PromiseObject>());
1919 1919 : if (promise->state() != JS::PromiseState::Pending)
1920 : return true;
1921 :
1922 0 : if (mode == ResolveMode) {
1923 1811 : if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION))
1924 : return true;
1925 1656 : return ResolvePromiseInternal(cx, promise, result);
1926 : }
1927 :
1928 26 : if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION))
1929 : return true;
1930 26 : return RejectMaybeWrappedPromise(cx, promiseObj, result);
1931 : }
1932 :
1933 : // ES2016, 25.4.4.1.1.
1934 : static MOZ_MUST_USE bool
1935 85 : PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
1936 : HandleObject promiseObj, HandleObject resolve, HandleObject reject,
1937 : bool* done)
1938 : {
1939 85 : *done = false;
1940 :
1941 0 : RootedObject unwrappedPromiseObj(cx);
1942 0 : if (IsWrapper(promiseObj)) {
1943 0 : unwrappedPromiseObj = CheckedUnwrap(promiseObj);
1944 0 : MOZ_ASSERT(unwrappedPromiseObj);
1945 : }
1946 :
1947 : // Step 1.
1948 0 : MOZ_ASSERT(C->isConstructor());
1949 255 : RootedValue CVal(cx, ObjectValue(*C));
1950 :
1951 : // Step 2 (omitted).
1952 :
1953 : // Step 3.
1954 : // We have to be very careful about which compartments we create things in
1955 : // here. In particular, we have to maintain the invariant that anything
1956 : // stored in a reserved slot is same-compartment with the object whose
1957 : // reserved slot it's in. But we want to create the values array in the
1958 : // Promise's compartment, because that array can get exposed to
1959 : // code that has access to the Promise (in particular code from
1960 : // that compartment), and that should work, even if the Promise
1961 : // compartment is less-privileged than our caller compartment.
1962 : //
1963 : // So the plan is as follows: Create the values array in the promise
1964 : // compartment. Create the PromiseAllResolveElement function
1965 : // and the data holder in our current compartment. Store a
1966 : // cross-compartment wrapper to the values array in the holder. This
1967 : // should be OK because the only things we hand the
1968 : // PromiseAllResolveElement function to are the "then" calls we do and in
1969 : // the case when the Promise's compartment is not the current compartment
1970 : // those are happening over Xrays anyway, which means they get the
1971 : // canonical "then" function and content can't see our
1972 : // PromiseAllResolveElement.
1973 0 : RootedObject valuesArray(cx);
1974 0 : if (unwrappedPromiseObj) {
1975 0 : JSAutoRealm ar(cx, unwrappedPromiseObj);
1976 0 : valuesArray = NewDenseFullyAllocatedArray(cx, 0);
1977 : } else {
1978 170 : valuesArray = NewDenseFullyAllocatedArray(cx, 0);
1979 : }
1980 85 : if (!valuesArray)
1981 : return false;
1982 :
1983 0 : RootedValue valuesArrayVal(cx, ObjectValue(*valuesArray));
1984 170 : if (!cx->compartment()->wrap(cx, &valuesArrayVal))
1985 : return false;
1986 :
1987 : // Step 4.
1988 : // Create our data holder that holds all the things shared across
1989 : // every step of the iterator. In particular, this holds the
1990 : // remainingElementsCount (as an integer reserved slot), the array of
1991 : // values, and the resolve function from our PromiseCapability.
1992 0 : Rooted<PromiseAllDataHolder*> dataHolder(cx, NewPromiseAllDataHolder(cx, promiseObj,
1993 0 : valuesArrayVal, resolve));
1994 85 : if (!dataHolder)
1995 : return false;
1996 255 : RootedValue dataHolderVal(cx, ObjectValue(*dataHolder));
1997 :
1998 : // Step 5.
1999 85 : uint32_t index = 0;
2000 :
2001 : // Step 6.
2002 0 : RootedValue nextValue(cx);
2003 0 : RootedId indexId(cx);
2004 255 : RootedValue rejectFunVal(cx, ObjectValue(*reject));
2005 :
2006 : while (true) {
2007 : // Steps a-c, e-g.
2008 315 : if (!iterator.next(&nextValue, done)) {
2009 : // Steps b, f.
2010 0 : *done = true;
2011 :
2012 : // Steps c, g.
2013 85 : return false;
2014 : }
2015 :
2016 : // Step d.
2017 315 : if (*done) {
2018 : // Step d.i (implicit).
2019 :
2020 : // Step d.ii.
2021 85 : int32_t remainingCount = dataHolder->decreaseRemainingCount();
2022 :
2023 : // Steps d.iii-iv.
2024 0 : if (remainingCount == 0) {
2025 0 : return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode,
2026 10 : promiseObj);
2027 : }
2028 :
2029 : // We're all set for now!
2030 : return true;
2031 : }
2032 :
2033 : // Step h.
2034 : { // Scope for the JSAutoRealm we need to work with valuesArray. We
2035 : // mostly do this for performance; we could go ahead and do the define via
2036 : // a cross-compartment proxy instead...
2037 0 : JSAutoRealm ar(cx, valuesArray);
2038 0 : indexId = INT_TO_JSID(index);
2039 1 : if (!DefineDataProperty(cx, valuesArray, indexId, UndefinedHandleValue))
2040 0 : return false;
2041 : }
2042 :
2043 : // Step i.
2044 : // Sadly, because someone could have overridden
2045 : // "resolve" on the canonical Promise constructor.
2046 0 : RootedValue nextPromise(cx);
2047 0 : RootedValue staticResolve(cx);
2048 0 : if (!GetProperty(cx, C, CVal, cx->names().resolve, &staticResolve))
2049 0 : return false;
2050 :
2051 0 : FixedInvokeArgs<1> resolveArgs(cx);
2052 0 : resolveArgs[0].set(nextValue);
2053 690 : if (!Call(cx, staticResolve, CVal, resolveArgs, &nextPromise))
2054 : return false;
2055 :
2056 : // Step j.
2057 460 : RootedFunction resolveFunc(cx, NewNativeFunction(cx, PromiseAllResolveElementFunction,
2058 : 1, nullptr,
2059 : gc::AllocKind::FUNCTION_EXTENDED,
2060 0 : GenericObject));
2061 0 : if (!resolveFunc)
2062 0 : return false;
2063 :
2064 : // Steps k,m,n.
2065 460 : resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, dataHolderVal);
2066 :
2067 : // Step l.
2068 0 : resolveFunc->setExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex,
2069 690 : Int32Value(index));
2070 :
2071 : // Steps o-p.
2072 230 : dataHolder->increaseRemainingCount();
2073 :
2074 : // Step q.
2075 0 : RootedValue resolveFunVal(cx, ObjectValue(*resolveFunc));
2076 0 : if (!BlockOnPromise(cx, nextPromise, promiseObj, resolveFunVal, rejectFunVal))
2077 0 : return false;
2078 :
2079 : // Step r.
2080 0 : index++;
2081 230 : MOZ_ASSERT(index > 0);
2082 : }
2083 : }
2084 :
2085 : // ES2016, 25.4.4.1.2.
2086 : static bool
2087 229 : PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp)
2088 : {
2089 229 : CallArgs args = CallArgsFromVp(argc, vp);
2090 :
2091 0 : RootedFunction resolve(cx, &args.callee().as<JSFunction>());
2092 458 : RootedValue xVal(cx, args.get(0));
2093 :
2094 : // Step 1.
2095 458 : RootedValue dataVal(cx, resolve->getExtendedSlot(PromiseAllResolveElementFunctionSlot_Data));
2096 :
2097 : // Step 2.
2098 : // We use the existence of the data holder as a signal for whether the
2099 : // Promise was already resolved. Upon resolution, it's reset to
2100 : // `undefined`.
2101 0 : if (dataVal.isUndefined()) {
2102 0 : args.rval().setUndefined();
2103 0 : return true;
2104 : }
2105 :
2106 458 : Rooted<PromiseAllDataHolder*> data(cx, &dataVal.toObject().as<PromiseAllDataHolder>());
2107 :
2108 : // Step 3.
2109 458 : resolve->setExtendedSlot(PromiseAllResolveElementFunctionSlot_Data, UndefinedValue());
2110 :
2111 : // Step 4.
2112 0 : int32_t index = resolve->getExtendedSlot(PromiseAllResolveElementFunctionSlot_ElementIndex)
2113 229 : .toInt32();
2114 :
2115 : // Step 5.
2116 0 : RootedValue valuesVal(cx, data->valuesArray());
2117 0 : RootedObject valuesObj(cx, &valuesVal.toObject());
2118 0 : bool valuesListIsWrapped = false;
2119 0 : if (IsWrapper(valuesObj)) {
2120 0 : valuesListIsWrapped = true;
2121 : // See comment for PerformPromiseAll, step 3 for why we unwrap here.
2122 0 : valuesObj = UncheckedUnwrap(valuesObj);
2123 : }
2124 0 : if (JS_IsDeadWrapper(valuesObj)) {
2125 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
2126 0 : return false;
2127 : }
2128 458 : RootedNativeObject values(cx, &valuesObj->as<NativeObject>());
2129 :
2130 : // Step 6 (moved under step 10).
2131 : // Step 7 (moved to step 9).
2132 :
2133 : // Step 8.
2134 : // The index is guaranteed to be initialized to `undefined`.
2135 0 : if (valuesListIsWrapped) {
2136 0 : AutoRealm ar(cx, values);
2137 0 : if (!cx->compartment()->wrap(cx, &xVal))
2138 0 : return false;
2139 : }
2140 458 : values->setDenseElement(index, xVal);
2141 :
2142 : // Steps 7,9.
2143 229 : uint32_t remainingCount = data->decreaseRemainingCount();
2144 :
2145 : // Step 10.
2146 229 : if (remainingCount == 0) {
2147 : // Step 10.a. (Omitted, happened in PerformPromiseAll.)
2148 : // Step 10.b.
2149 :
2150 : // Step 6 (Adapted to work with PromiseAllDataHolder's layout).
2151 0 : RootedObject resolveAllFun(cx, data->resolveObj());
2152 0 : RootedObject promiseObj(cx, data->promiseObj());
2153 1 : if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj))
2154 0 : return false;
2155 : }
2156 :
2157 : // Step 11.
2158 0 : args.rval().setUndefined();
2159 229 : return true;
2160 : }
2161 :
2162 : static MOZ_MUST_USE bool PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator,
2163 : HandleObject C, HandleObject promiseObj,
2164 : HandleObject resolve, HandleObject reject,
2165 : bool* done);
2166 :
2167 : // ES2016, 25.4.4.3.
2168 : static bool
2169 8 : Promise_static_race(JSContext* cx, unsigned argc, Value* vp)
2170 : {
2171 0 : CallArgs args = CallArgsFromVp(argc, vp);
2172 16 : RootedValue iterable(cx, args.get(0));
2173 :
2174 : // Step 2 (reordered).
2175 0 : RootedValue CVal(cx, args.thisv());
2176 8 : if (!CVal.isObject()) {
2177 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
2178 0 : "Receiver of Promise.race call");
2179 0 : return false;
2180 : }
2181 :
2182 : // Step 1.
2183 16 : RootedObject C(cx, &CVal.toObject());
2184 :
2185 : // Step 3.
2186 0 : RootedObject resultPromise(cx);
2187 0 : RootedObject resolve(cx);
2188 0 : RootedObject reject(cx);
2189 32 : if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, false))
2190 : return false;
2191 :
2192 : // Steps 4-5.
2193 0 : JS::ForOfIterator iter(cx);
2194 0 : if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable))
2195 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
2196 :
2197 8 : if (!iter.valueIsIterable()) {
2198 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE,
2199 0 : "Argument of Promise.race");
2200 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
2201 : }
2202 :
2203 : // Step 6 (implicit).
2204 :
2205 : // Step 7.
2206 : bool done;
2207 32 : bool result = PerformPromiseRace(cx, iter, C, resultPromise, resolve, reject, &done);
2208 :
2209 : // Step 8.
2210 8 : if (!result) {
2211 : // Step 8.a.
2212 0 : if (!done)
2213 0 : iter.closeThrow();
2214 :
2215 : // Step 8.b.
2216 0 : return AbruptRejectPromise(cx, args, resultPromise, reject);
2217 : }
2218 :
2219 : // Step 9.
2220 0 : args.rval().setObject(*resultPromise);
2221 8 : return true;
2222 : }
2223 :
2224 : // ES2016, 25.4.4.3.1.
2225 : static MOZ_MUST_USE bool
2226 8 : PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C,
2227 : HandleObject promiseObj, HandleObject resolve, HandleObject reject,
2228 : bool* done)
2229 : {
2230 0 : *done = false;
2231 0 : MOZ_ASSERT(C->isConstructor());
2232 24 : RootedValue CVal(cx, ObjectValue(*C));
2233 :
2234 0 : RootedValue nextValue(cx);
2235 0 : RootedValue resolveFunVal(cx, ObjectValue(*resolve));
2236 24 : RootedValue rejectFunVal(cx, ObjectValue(*reject));
2237 :
2238 : while (true) {
2239 : // Steps a-c, e-g.
2240 20 : if (!iterator.next(&nextValue, done)) {
2241 : // Steps b, f.
2242 0 : *done = true;
2243 :
2244 : // Steps c, g.
2245 8 : return false;
2246 : }
2247 :
2248 : // Step d.
2249 20 : if (*done) {
2250 : // Step d.i (implicit).
2251 :
2252 : // Step d.ii.
2253 : return true;
2254 : }
2255 :
2256 : // Step h.
2257 : // Sadly, because someone could have overridden
2258 : // "resolve" on the canonical Promise constructor.
2259 0 : RootedValue nextPromise(cx);
2260 0 : RootedValue staticResolve(cx);
2261 0 : if (!GetProperty(cx, C, CVal, cx->names().resolve, &staticResolve))
2262 0 : return false;
2263 :
2264 0 : FixedInvokeArgs<1> resolveArgs(cx);
2265 0 : resolveArgs[0].set(nextValue);
2266 36 : if (!Call(cx, staticResolve, CVal, resolveArgs, &nextPromise))
2267 : return false;
2268 :
2269 : // Step i.
2270 36 : if (!BlockOnPromise(cx, nextPromise, promiseObj, resolveFunVal, rejectFunVal))
2271 : return false;
2272 : }
2273 :
2274 : MOZ_ASSERT_UNREACHABLE("Shouldn't reach the end of PerformPromiseRace");
2275 : }
2276 :
2277 :
2278 : // ES2016, Sub-steps of 25.4.4.4 and 25.4.4.5.
2279 : static MOZ_MUST_USE JSObject*
2280 335 : CommonStaticResolveRejectImpl(JSContext* cx, HandleValue thisVal, HandleValue argVal,
2281 : ResolutionMode mode)
2282 : {
2283 : // Steps 1-2.
2284 335 : if (!thisVal.isObject()) {
2285 : const char* msg = mode == ResolveMode
2286 0 : ? "Receiver of Promise.resolve call"
2287 0 : : "Receiver of Promise.reject call";
2288 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, msg);
2289 0 : return nullptr;
2290 : }
2291 670 : RootedObject C(cx, &thisVal.toObject());
2292 :
2293 : // Step 3 of Resolve.
2294 0 : if (mode == ResolveMode && argVal.isObject()) {
2295 0 : RootedObject xObj(cx, &argVal.toObject());
2296 0 : bool isPromise = false;
2297 496 : if (xObj->is<PromiseObject>()) {
2298 : isPromise = true;
2299 50 : } else if (IsWrapper(xObj)) {
2300 : // Treat instances of Promise from other compartments as Promises
2301 : // here, too.
2302 : // It's important to do the GetProperty for the `constructor`
2303 : // below through the wrapper, because wrappers can change the
2304 : // outcome, so instead of unwrapping and then performing the
2305 : // GetProperty, just check here and then operate on the original
2306 : // object again.
2307 0 : RootedObject unwrappedObject(cx, CheckedUnwrap(xObj));
2308 0 : if (unwrappedObject && unwrappedObject->is<PromiseObject>())
2309 1 : isPromise = true;
2310 : }
2311 0 : if (isPromise) {
2312 0 : RootedValue ctorVal(cx);
2313 0 : if (!GetProperty(cx, xObj, xObj, cx->names().constructor, &ctorVal))
2314 0 : return nullptr;
2315 0 : if (ctorVal == thisVal)
2316 198 : return xObj;
2317 : }
2318 : }
2319 :
2320 : // Step 4 of Resolve, 3 of Reject.
2321 0 : RootedObject promise(cx);
2322 0 : RootedObject resolveFun(cx);
2323 0 : RootedObject rejectFun(cx);
2324 548 : if (!NewPromiseCapability(cx, C, &promise, &resolveFun, &rejectFun, true))
2325 : return nullptr;
2326 :
2327 : // Step 5 of Resolve, 4 of Reject.
2328 274 : if (!RunResolutionFunction(cx, mode == ResolveMode ? resolveFun : rejectFun, argVal, mode,
2329 : promise))
2330 : {
2331 : return nullptr;
2332 : }
2333 :
2334 : // Step 6 of Resolve, 4 of Reject.
2335 137 : return promise;
2336 : }
2337 :
2338 : MOZ_MUST_USE JSObject*
2339 0 : js::PromiseResolve(JSContext* cx, HandleObject constructor, HandleValue value)
2340 : {
2341 0 : RootedValue C(cx, ObjectValue(*constructor));
2342 0 : return CommonStaticResolveRejectImpl(cx, C, value, ResolveMode);
2343 : }
2344 :
2345 : /**
2346 : * ES2016, 25.4.4.4, Promise.reject.
2347 : */
2348 : static bool
2349 0 : Promise_reject(JSContext* cx, unsigned argc, Value* vp)
2350 : {
2351 0 : CallArgs args = CallArgsFromVp(argc, vp);
2352 0 : RootedValue thisVal(cx, args.thisv());
2353 0 : RootedValue argVal(cx, args.get(0));
2354 0 : JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, RejectMode);
2355 0 : if (!result)
2356 : return false;
2357 0 : args.rval().setObject(*result);
2358 0 : return true;
2359 : }
2360 :
2361 : /**
2362 : * Unforgeable version of ES2016, 25.4.4.4, Promise.reject.
2363 : */
2364 : /* static */ JSObject*
2365 0 : PromiseObject::unforgeableReject(JSContext* cx, HandleValue value)
2366 : {
2367 0 : RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx));
2368 0 : if (!promiseCtor)
2369 : return nullptr;
2370 0 : RootedValue cVal(cx, ObjectValue(*promiseCtor));
2371 0 : return CommonStaticResolveRejectImpl(cx, cVal, value, RejectMode);
2372 : }
2373 :
2374 : /**
2375 : * ES2016, 25.4.4.5, Promise.resolve.
2376 : */
2377 : static bool
2378 334 : Promise_static_resolve(JSContext* cx, unsigned argc, Value* vp)
2379 : {
2380 0 : CallArgs args = CallArgsFromVp(argc, vp);
2381 0 : RootedValue thisVal(cx, args.thisv());
2382 0 : RootedValue argVal(cx, args.get(0));
2383 0 : JSObject* result = CommonStaticResolveRejectImpl(cx, thisVal, argVal, ResolveMode);
2384 334 : if (!result)
2385 : return false;
2386 0 : args.rval().setObject(*result);
2387 334 : return true;
2388 : }
2389 :
2390 : /**
2391 : * Unforgeable version of ES2016, 25.4.4.5, Promise.resolve.
2392 : */
2393 : /* static */ JSObject*
2394 1 : PromiseObject::unforgeableResolve(JSContext* cx, HandleValue value)
2395 : {
2396 0 : RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx));
2397 1 : if (!promiseCtor)
2398 : return nullptr;
2399 0 : RootedValue cVal(cx, ObjectValue(*promiseCtor));
2400 1 : return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode);
2401 : }
2402 :
2403 : /**
2404 : * ES2016, 25.4.4.6 get Promise [ @@species ]
2405 : */
2406 : static bool
2407 4 : Promise_static_species(JSContext* cx, unsigned argc, Value* vp)
2408 : {
2409 4 : CallArgs args = CallArgsFromVp(argc, vp);
2410 :
2411 : // Step 1: Return the this value.
2412 0 : args.rval().set(args.thisv());
2413 4 : return true;
2414 : }
2415 :
2416 : // ES2016, 25.4.5.1, implemented in Promise.js.
2417 :
2418 : static PromiseReactionRecord*
2419 3143 : NewReactionRecord(JSContext* cx, HandleObject resultPromise, HandleValue onFulfilled,
2420 : HandleValue onRejected, HandleObject resolve, HandleObject reject,
2421 : HandleObject incumbentGlobalObject)
2422 : {
2423 : // Either of the following conditions must be met:
2424 : // * resultPromise is a PromiseObject
2425 : // * resolve and reject are callable
2426 : // except for Async Generator, there resultPromise can be nullptr.
2427 0 : MOZ_ASSERT_IF(resultPromise && !resultPromise->is<PromiseObject>(), resolve);
2428 0 : MOZ_ASSERT_IF(resultPromise && !resultPromise->is<PromiseObject>(), IsCallable(resolve));
2429 0 : MOZ_ASSERT_IF(resultPromise && !resultPromise->is<PromiseObject>(), reject);
2430 9171 : MOZ_ASSERT_IF(resultPromise && !resultPromise->is<PromiseObject>(), IsCallable(reject));
2431 :
2432 0 : Rooted<PromiseReactionRecord*> reaction(cx, NewObjectWithClassProto<PromiseReactionRecord>(cx));
2433 3143 : if (!reaction)
2434 : return nullptr;
2435 :
2436 0 : assertSameCompartment(cx, resultPromise);
2437 0 : assertSameCompartment(cx, onFulfilled);
2438 0 : assertSameCompartment(cx, onRejected);
2439 0 : assertSameCompartment(cx, resolve);
2440 0 : assertSameCompartment(cx, reject);
2441 3143 : assertSameCompartment(cx, incumbentGlobalObject);
2442 :
2443 0 : reaction->setFixedSlot(ReactionRecordSlot_Promise, ObjectOrNullValue(resultPromise));
2444 0 : reaction->setFixedSlot(ReactionRecordSlot_Flags, Int32Value(0));
2445 0 : reaction->setFixedSlot(ReactionRecordSlot_OnFulfilled, onFulfilled);
2446 0 : reaction->setFixedSlot(ReactionRecordSlot_OnRejected, onRejected);
2447 0 : reaction->setFixedSlot(ReactionRecordSlot_Resolve, ObjectOrNullValue(resolve));
2448 0 : reaction->setFixedSlot(ReactionRecordSlot_Reject, ObjectOrNullValue(reject));
2449 0 : reaction->setFixedSlot(ReactionRecordSlot_IncumbentGlobalObject,
2450 9429 : ObjectOrNullValue(incumbentGlobalObject));
2451 :
2452 3143 : return reaction;
2453 : }
2454 :
2455 : static bool
2456 2071 : IsPromiseSpecies(JSContext* cx, JSFunction* species)
2457 : {
2458 2071 : return species->maybeNative() == Promise_static_species;
2459 : }
2460 :
2461 : // ES2016, 25.4.5.3., steps 3-5.
2462 : MOZ_MUST_USE bool
2463 1834 : js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
2464 : HandleValue onFulfilled, HandleValue onRejected,
2465 : MutableHandleObject dependent, CreateDependentPromise createDependent)
2466 : {
2467 0 : RootedObject promiseObj(cx, promise);
2468 0 : if (promise->compartment() != cx->compartment()) {
2469 4 : if (!cx->compartment()->wrap(cx, &promiseObj))
2470 : return false;
2471 : }
2472 :
2473 0 : RootedObject resultPromise(cx);
2474 0 : RootedObject resolve(cx);
2475 3668 : RootedObject reject(cx);
2476 :
2477 1834 : if (createDependent != CreateDependentPromise::Never) {
2478 : // Step 3.
2479 0 : RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
2480 0 : if (!C)
2481 0 : return false;
2482 :
2483 0 : if (createDependent == CreateDependentPromise::Always ||
2484 132 : !IsNativeFunction(C, PromiseConstructor))
2485 : {
2486 : // Step 4.
2487 6812 : if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, true))
2488 : return false;
2489 : }
2490 : }
2491 :
2492 : // Step 5.
2493 5502 : if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
2494 : return false;
2495 :
2496 0 : dependent.set(resultPromise);
2497 1834 : return true;
2498 : }
2499 :
2500 : static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx,
2501 : Handle<PromiseObject*> promise,
2502 : Handle<PromiseReactionRecord*> reaction);
2503 :
2504 : // Some async/await functions are implemented here instead of
2505 : // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions.
2506 :
2507 : // ES 2018 draft 14.6.11 and 14.7.14 step 1.
2508 : MOZ_MUST_USE PromiseObject*
2509 953 : js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
2510 : {
2511 : // Step 1.
2512 0 : PromiseObject* promise = CreatePromiseObjectWithoutResolutionFunctions(cx);
2513 953 : if (!promise)
2514 : return nullptr;
2515 :
2516 0 : AddPromiseFlags(*promise, PROMISE_FLAG_ASYNC);
2517 0 : promise->setFixedSlot(PromiseSlot_AwaitGenerator, generatorVal);
2518 953 : return promise;
2519 : }
2520 :
2521 : bool
2522 0 : js::IsPromiseForAsync(JSObject* promise)
2523 : {
2524 0 : return promise->is<PromiseObject>() &&
2525 0 : PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC);
2526 : }
2527 :
2528 : // ES 2018 draft 25.5.5.2 steps 3.f, 3.g.
2529 : MOZ_MUST_USE bool
2530 20 : js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
2531 : {
2532 : // Step 3.f.
2533 0 : RootedValue exc(cx);
2534 20 : if (!MaybeGetAndClearException(cx, &exc))
2535 : return false;
2536 :
2537 40 : if (!RejectMaybeWrappedPromise(cx, resultPromise, exc))
2538 : return false;
2539 :
2540 : // Step 3.g.
2541 20 : return true;
2542 : }
2543 :
2544 : // ES 2018 draft 25.5.5.2 steps 3.d-e, 3.g.
2545 : MOZ_MUST_USE bool
2546 900 : js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
2547 : {
2548 : // Steps 3.d-e.
2549 900 : if (!ResolvePromiseInternal(cx, resultPromise, value))
2550 : return false;
2551 :
2552 : // Step 3.g.
2553 900 : return true;
2554 : }
2555 :
2556 : // Helper function that performs the equivalent steps as
2557 : // Async Iteration proposal 4.1 Await steps 2-3, 6-9 or similar.
2558 : template <typename T>
2559 : static MOZ_MUST_USE bool
2560 1067 : InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise,
2561 : HandleValue onFulfilled, HandleValue onRejected, T extraStep)
2562 : {
2563 0 : MOZ_ASSERT(onFulfilled.isNumber() || onFulfilled.isObject());
2564 1067 : MOZ_ASSERT(onRejected.isNumber() || onRejected.isObject());
2565 :
2566 : // Step 2.
2567 0 : Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
2568 1067 : if (!promise)
2569 : return false;
2570 :
2571 : // Step 3.
2572 1067 : if (!ResolvePromiseInternal(cx, promise, value))
2573 : return false;
2574 :
2575 0 : RootedObject incumbentGlobal(cx);
2576 1067 : if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
2577 : return false;
2578 :
2579 : // Step 7-8.
2580 4268 : Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise,
2581 : onFulfilled, onRejected,
2582 : nullptr, nullptr,
2583 0 : incumbentGlobal));
2584 1067 : if (!reaction)
2585 : return false;
2586 :
2587 : // Step 6.
2588 2134 : extraStep(reaction);
2589 :
2590 : // Step 9.
2591 2134 : return PerformPromiseThenWithReaction(cx, promise, reaction);
2592 : }
2593 :
2594 : // ES 2018 draft 25.5.5.3 steps 2-10.
2595 : MOZ_MUST_USE bool
2596 1067 : js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
2597 : {
2598 : // Steps 4-5.
2599 0 : RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled));
2600 2134 : RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected));
2601 :
2602 : // Steps 2-3, 6-10.
2603 : auto extra = [](Handle<PromiseReactionRecord*> reaction) {
2604 1067 : reaction->setIsAsyncFunction();
2605 : };
2606 4268 : return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra);
2607 : }
2608 :
2609 : // Async Iteration proposal 4.1 Await steps 2-9.
2610 : MOZ_MUST_USE bool
2611 0 : js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
2612 : HandleValue value)
2613 : {
2614 : // Steps 4-5.
2615 0 : RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled));
2616 0 : RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected));
2617 :
2618 : // Steps 2-3, 6-9.
2619 : auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
2620 0 : reaction->setIsAsyncGenerator(asyncGenObj);
2621 0 : };
2622 0 : return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra);
2623 : }
2624 :
2625 : // Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next
2626 : // Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return
2627 : // Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw
2628 : bool
2629 0 : js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind)
2630 : {
2631 : // Step 1.
2632 0 : RootedValue thisVal(cx, args.thisv());
2633 :
2634 : // Step 2.
2635 0 : RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
2636 0 : if (!resultPromise)
2637 : return false;
2638 :
2639 : // Step 3.
2640 0 : if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) {
2641 : // NB: See https://github.com/tc39/proposal-async-iteration/issues/105
2642 : // for why this check shouldn't be necessary as long as we can ensure
2643 : // the Async-from-Sync iterator can't be accessed directly by user
2644 : // code.
2645 :
2646 : // Step 3.a.
2647 0 : RootedValue badGeneratorError(cx);
2648 0 : if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError))
2649 : return false;
2650 :
2651 : // Step 3.b.
2652 0 : if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
2653 : return false;
2654 :
2655 : // Step 3.c.
2656 0 : args.rval().setObject(*resultPromise);
2657 0 : return true;
2658 : }
2659 :
2660 : Rooted<AsyncFromSyncIteratorObject*> asyncIter(
2661 0 : cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>());
2662 :
2663 : // Step 4.
2664 0 : RootedObject iter(cx, asyncIter->iterator());
2665 :
2666 0 : RootedValue resultVal(cx);
2667 0 : RootedValue func(cx);
2668 0 : if (completionKind == CompletionKind::Normal) {
2669 : // 11.1.3.2.1 steps 5-6 (partially).
2670 0 : func.set(asyncIter->nextMethod());
2671 0 : } else if (completionKind == CompletionKind::Return) {
2672 : // 11.1.3.2.2 steps 5-6.
2673 0 : if (!GetProperty(cx, iter, iter, cx->names().return_, &func))
2674 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2675 :
2676 : // Step 7.
2677 0 : if (func.isNullOrUndefined()) {
2678 : // Step 7.a.
2679 0 : RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true));
2680 0 : if (!resultObj)
2681 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2682 :
2683 0 : RootedValue resultVal(cx, ObjectValue(*resultObj));
2684 :
2685 : // Step 7.b.
2686 0 : if (!ResolvePromiseInternal(cx, resultPromise, resultVal))
2687 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2688 :
2689 : // Step 7.c.
2690 0 : args.rval().setObject(*resultPromise);
2691 0 : return true;
2692 : }
2693 : } else {
2694 : // 11.1.3.2.3 steps 5-6.
2695 0 : MOZ_ASSERT(completionKind == CompletionKind::Throw);
2696 0 : if (!GetProperty(cx, iter, iter, cx->names().throw_, &func))
2697 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2698 :
2699 : // Step 7.
2700 0 : if (func.isNullOrUndefined()) {
2701 : // Step 7.a.
2702 0 : if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0)))
2703 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2704 :
2705 : // Step 7.b.
2706 0 : args.rval().setObject(*resultPromise);
2707 0 : return true;
2708 : }
2709 : }
2710 :
2711 : // 11.1.3.2.1 steps 5-6 (partially).
2712 : // 11.1.3.2.2, 11.1.3.2.3 steps 8-9.
2713 0 : RootedValue iterVal(cx, ObjectValue(*iter));
2714 0 : FixedInvokeArgs<1> args2(cx);
2715 0 : args2[0].set(args.get(0));
2716 0 : if (!js::Call(cx, func, iterVal, args2, &resultVal))
2717 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2718 :
2719 : // 11.1.3.2.1 steps 5-6 (partially).
2720 : // 11.1.3.2.2, 11.1.3.2.3 steps 10.
2721 0 : if (!resultVal.isObject()) {
2722 : CheckIsObjectKind kind;
2723 0 : switch (completionKind) {
2724 : case CompletionKind::Normal:
2725 0 : kind = CheckIsObjectKind::IteratorNext;
2726 0 : break;
2727 : case CompletionKind::Throw:
2728 0 : kind = CheckIsObjectKind::IteratorThrow;
2729 0 : break;
2730 : case CompletionKind::Return:
2731 0 : kind = CheckIsObjectKind::IteratorReturn;
2732 0 : break;
2733 : }
2734 0 : MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind));
2735 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2736 : }
2737 :
2738 0 : RootedObject resultObj(cx, &resultVal.toObject());
2739 :
2740 : // Following step numbers are for 11.1.3.2.1.
2741 : // For 11.1.3.2.2 and 11.1.3.2.3, steps 7-16 corresponds to steps 11-20.
2742 :
2743 : // Steps 7-8.
2744 0 : RootedValue doneVal(cx);
2745 0 : if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
2746 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2747 0 : bool done = ToBoolean(doneVal);
2748 :
2749 : // Steps 9-10.
2750 0 : RootedValue value(cx);
2751 0 : if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
2752 0 : return AbruptRejectPromise(cx, args, resultPromise, nullptr);
2753 :
2754 : // Steps 13-14.
2755 0 : RootedValue onFulfilled(cx, Int32Value(done
2756 : ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone
2757 0 : : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone));
2758 0 : RootedValue onRejected(cx, Int32Value(PromiseHandlerThrower));
2759 :
2760 : // Steps 11-12, 15.
2761 : auto extra = [](Handle<PromiseReactionRecord*> reaction) {
2762 : };
2763 0 : if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra))
2764 : return false;
2765 :
2766 : // Step 16.
2767 0 : args.rval().setObject(*resultPromise);
2768 0 : return true;
2769 : }
2770 :
2771 : enum class ResumeNextKind {
2772 : Enqueue, Reject, Resolve
2773 : };
2774 :
2775 : static MOZ_MUST_USE bool
2776 : AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
2777 : ResumeNextKind kind, HandleValue valueOrException = UndefinedHandleValue,
2778 : bool done = false);
2779 :
2780 : // Async Iteration proposal 11.4.3.3.
2781 : MOZ_MUST_USE bool
2782 0 : js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
2783 : HandleValue value, bool done)
2784 : {
2785 0 : return AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Resolve, value, done);
2786 : }
2787 :
2788 : // Async Iteration proposal 11.4.3.4.
2789 : MOZ_MUST_USE bool
2790 0 : js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
2791 : HandleValue exception)
2792 : {
2793 0 : return AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Reject, exception);
2794 : }
2795 :
2796 : // Async Iteration proposal 11.4.3.5.
2797 : static MOZ_MUST_USE bool
2798 0 : AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
2799 : ResumeNextKind kind,
2800 : HandleValue valueOrException_ /* = UndefinedHandleValue */,
2801 : bool done /* = false */)
2802 : {
2803 0 : RootedValue valueOrException(cx, valueOrException_);
2804 :
2805 : while (true) {
2806 0 : switch (kind) {
2807 : case ResumeNextKind::Enqueue:
2808 : // No further action required.
2809 : break;
2810 : case ResumeNextKind::Reject: {
2811 : // 11.4.3.4 AsyncGeneratorReject ( generator, exception )
2812 0 : HandleValue exception = valueOrException;
2813 :
2814 : // Step 1 (implicit).
2815 :
2816 : // Steps 2-3.
2817 0 : MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
2818 :
2819 : // Step 4.
2820 : Rooted<AsyncGeneratorRequest*> request(
2821 0 : cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
2822 0 : if (!request)
2823 0 : return false;
2824 :
2825 : // Step 5.
2826 0 : RootedObject resultPromise(cx, request->promise());
2827 :
2828 0 : asyncGenObj->cacheRequest(request);
2829 :
2830 : // Step 6.
2831 0 : if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
2832 0 : return false;
2833 :
2834 : // Steps 7-8.
2835 0 : break;
2836 : }
2837 : case ResumeNextKind::Resolve: {
2838 : // 11.4.3.3 AsyncGeneratorResolve ( generator, value, done )
2839 0 : HandleValue value = valueOrException;
2840 :
2841 : // Step 1 (implicit).
2842 :
2843 : // Steps 2-3.
2844 0 : MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
2845 :
2846 : // Step 4.
2847 : Rooted<AsyncGeneratorRequest*> request(
2848 0 : cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
2849 0 : if (!request)
2850 0 : return false;
2851 :
2852 : // Step 5.
2853 0 : RootedObject resultPromise(cx, request->promise());
2854 :
2855 0 : asyncGenObj->cacheRequest(request);
2856 :
2857 : // Step 6.
2858 0 : RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
2859 0 : if (!resultObj)
2860 0 : return false;
2861 :
2862 0 : RootedValue resultValue(cx, ObjectValue(*resultObj));
2863 :
2864 : // Step 7.
2865 0 : if (!ResolvePromiseInternal(cx, resultPromise, resultValue))
2866 0 : return false;
2867 :
2868 : // Steps 8-9.
2869 0 : break;
2870 : }
2871 : }
2872 :
2873 : // Step 1 (implicit).
2874 :
2875 : // Steps 2-3.
2876 0 : MOZ_ASSERT(!asyncGenObj->isExecuting());
2877 :
2878 : // Step 4.
2879 0 : if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn())
2880 : return true;
2881 :
2882 : // Steps 5-6.
2883 0 : if (asyncGenObj->isQueueEmpty())
2884 : return true;
2885 :
2886 : // Steps 7-8.
2887 : Rooted<AsyncGeneratorRequest*> request(
2888 0 : cx, AsyncGeneratorObject::peekRequest(asyncGenObj));
2889 0 : if (!request)
2890 0 : return false;
2891 :
2892 : // Step 9.
2893 0 : CompletionKind completionKind = request->completionKind();
2894 :
2895 : // Step 10.
2896 0 : if (completionKind != CompletionKind::Normal) {
2897 : // Step 10.a.
2898 0 : if (asyncGenObj->isSuspendedStart())
2899 0 : asyncGenObj->setCompleted();
2900 :
2901 : // Step 10.b.
2902 0 : if (asyncGenObj->isCompleted()) {
2903 0 : RootedValue value(cx, request->completionValue());
2904 :
2905 : // Step 10.b.i.
2906 0 : if (completionKind == CompletionKind::Return) {
2907 : // Steps 10.b.i.1.
2908 0 : asyncGenObj->setAwaitingReturn();
2909 :
2910 : // Steps 10.b.i.4-6 (reordered).
2911 : static constexpr int32_t ResumeNextReturnFulfilled =
2912 : PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled;
2913 : static constexpr int32_t ResumeNextReturnRejected =
2914 : PromiseHandlerAsyncGeneratorResumeNextReturnRejected;
2915 :
2916 0 : RootedValue onFulfilled(cx, Int32Value(ResumeNextReturnFulfilled));
2917 0 : RootedValue onRejected(cx, Int32Value(ResumeNextReturnRejected));
2918 :
2919 : // Steps 10.b.i.2-3, 7-10.
2920 : auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
2921 0 : reaction->setIsAsyncGenerator(asyncGenObj);
2922 0 : };
2923 0 : return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra);
2924 : }
2925 :
2926 : // Step 10.b.ii.1.
2927 0 : MOZ_ASSERT(completionKind == CompletionKind::Throw);
2928 :
2929 : // Steps 10.b.ii.2-3.
2930 0 : kind = ResumeNextKind::Reject;
2931 0 : valueOrException.set(value);
2932 : // |done| is unused for ResumeNextKind::Reject.
2933 0 : continue;
2934 : }
2935 0 : } else if (asyncGenObj->isCompleted()) {
2936 : // Step 11.
2937 0 : kind = ResumeNextKind::Resolve;
2938 0 : valueOrException.setUndefined();
2939 0 : done = true;
2940 0 : continue;
2941 : }
2942 :
2943 : // Step 12.
2944 0 : MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield());
2945 :
2946 : // Step 16 (reordered).
2947 0 : asyncGenObj->setExecuting();
2948 :
2949 0 : RootedValue argument(cx, request->completionValue());
2950 :
2951 0 : if (completionKind == CompletionKind::Return) {
2952 : // 11.4.3.7 AsyncGeneratorYield step 8.b-e.
2953 : // Since we don't have the place that handles return from yield
2954 : // inside the generator, handle the case here, with extra state
2955 : // State_AwaitingYieldReturn.
2956 0 : asyncGenObj->setAwaitingYieldReturn();
2957 :
2958 : static constexpr int32_t YieldReturnAwaitedFulfilled =
2959 : PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled;
2960 : static constexpr int32_t YieldReturnAwaitedRejected =
2961 : PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected;
2962 :
2963 0 : RootedValue onFulfilled(cx, Int32Value(YieldReturnAwaitedFulfilled));
2964 0 : RootedValue onRejected(cx, Int32Value(YieldReturnAwaitedRejected));
2965 :
2966 : auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
2967 0 : reaction->setIsAsyncGenerator(asyncGenObj);
2968 0 : };
2969 0 : return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra);
2970 : }
2971 :
2972 : // Steps 13-15, 17-21.
2973 0 : return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument);
2974 : }
2975 : }
2976 :
2977 : // Async Iteration proposal 11.4.3.6.
2978 : MOZ_MUST_USE bool
2979 0 : js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
2980 : CompletionKind completionKind, HandleValue completionValue,
2981 : MutableHandleValue result)
2982 : {
2983 : // Step 1 (implicit).
2984 :
2985 : // Step 2.
2986 0 : RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
2987 0 : if (!resultPromise)
2988 : return false;
2989 :
2990 : // Step 3.
2991 0 : if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is<AsyncGeneratorObject>()) {
2992 : // Step 3.a.
2993 0 : RootedValue badGeneratorError(cx);
2994 0 : if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError))
2995 : return false;
2996 :
2997 : // Step 3.b.
2998 0 : if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
2999 : return false;
3000 :
3001 : // Step 3.c.
3002 0 : result.setObject(*resultPromise);
3003 0 : return true;
3004 : }
3005 :
3006 : Rooted<AsyncGeneratorObject*> asyncGenObj(
3007 0 : cx, &asyncGenVal.toObject().as<AsyncGeneratorObject>());
3008 :
3009 : // Step 5 (reordered).
3010 : Rooted<AsyncGeneratorRequest*> request(
3011 0 : cx, AsyncGeneratorObject::createRequest(cx, asyncGenObj, completionKind, completionValue,
3012 0 : resultPromise));
3013 0 : if (!request)
3014 : return false;
3015 :
3016 : // Steps 4, 6.
3017 0 : if (!AsyncGeneratorObject::enqueueRequest(cx, asyncGenObj, request))
3018 : return false;
3019 :
3020 : // Step 7.
3021 0 : if (!asyncGenObj->isExecuting()) {
3022 : // Step 8.
3023 0 : if (!AsyncGeneratorResumeNext(cx, asyncGenObj, ResumeNextKind::Enqueue))
3024 : return false;
3025 : }
3026 :
3027 : // Step 9.
3028 0 : result.setObject(*resultPromise);
3029 0 : return true;
3030 : }
3031 :
3032 : static bool Promise_then(JSContext* cx, unsigned argc, Value* vp);
3033 : static bool Promise_then_impl(JSContext* cx, HandleValue promiseVal, HandleValue onFulfilled,
3034 : HandleValue onRejected, MutableHandleValue rval, bool rvalUsed);
3035 :
3036 : static bool
3037 198 : Promise_catch_impl(JSContext* cx, unsigned argc, Value* vp, bool rvalUsed)
3038 : {
3039 198 : CallArgs args = CallArgsFromVp(argc, vp);
3040 :
3041 : // Step 1.
3042 0 : RootedValue thenVal(cx);
3043 792 : if (!GetProperty(cx, args.thisv(), cx->names().then, &thenVal))
3044 : return false;
3045 :
3046 0 : if (IsNativeFunction(thenVal, &Promise_then)) {
3047 0 : return Promise_then_impl(cx, args.thisv(), UndefinedHandleValue, args.get(0),
3048 198 : args.rval(), rvalUsed);
3049 : }
3050 :
3051 0 : FixedInvokeArgs<2> iargs(cx);
3052 0 : iargs[0].setUndefined();
3053 0 : iargs[1].set(args.get(0));
3054 :
3055 0 : return Call(cx, thenVal, args.thisv(), iargs, args.rval());
3056 : }
3057 :
3058 : static MOZ_ALWAYS_INLINE bool
3059 132 : IsPromiseThenOrCatchRetValImplicitlyUsed(JSContext* cx)
3060 : {
3061 : // The returned promise of Promise#then and Promise#catch contains
3062 : // stack info if async stack is enabled. Even if their return value is not
3063 : // used explicitly in the script, the stack info is observable in devtools
3064 : // and profilers. We shouldn't apply the optimization not to allocate the
3065 : // returned Promise object if the it's implicitly used by them.
3066 : //
3067 : // FIXME: Once bug 1280819 gets fixed, we can use ShouldCaptureDebugInfo.
3068 264 : if (!cx->options().asyncStack())
3069 : return false;
3070 :
3071 : // If devtools is opened, the current realm will become debuggee.
3072 264 : if (cx->realm()->isDebuggee())
3073 : return true;
3074 :
3075 : // There are 2 profilers, and they can be independently enabled.
3076 264 : if (cx->runtime()->geckoProfiler().enabled())
3077 : return true;
3078 132 : if (JS::IsProfileTimelineRecordingEnabled())
3079 : return true;
3080 :
3081 : // The stack is also observable from Error#stack, but we don't care since
3082 : // it's nonstandard feature.
3083 132 : return false;
3084 : }
3085 :
3086 : // ES2016, 25.4.5.3.
3087 : static bool
3088 11 : Promise_catch_noRetVal(JSContext* cx, unsigned argc, Value* vp)
3089 : {
3090 11 : return Promise_catch_impl(cx, argc, vp, IsPromiseThenOrCatchRetValImplicitlyUsed(cx));
3091 : }
3092 :
3093 : // ES2016, 25.4.5.3.
3094 : static bool
3095 187 : Promise_catch(JSContext* cx, unsigned argc, Value* vp)
3096 : {
3097 187 : return Promise_catch_impl(cx, argc, vp, true);
3098 : }
3099 :
3100 : static bool
3101 1833 : Promise_then_impl(JSContext* cx, HandleValue promiseVal, HandleValue onFulfilled,
3102 : HandleValue onRejected, MutableHandleValue rval, bool rvalUsed)
3103 : {
3104 : // Step 1 (implicit).
3105 : // Step 2.
3106 1833 : if (!promiseVal.isObject()) {
3107 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
3108 0 : "Receiver of Promise.prototype.then call");
3109 0 : return false;
3110 : }
3111 0 : RootedObject promiseObj(cx, &promiseVal.toObject());
3112 3666 : Rooted<PromiseObject*> promise(cx);
3113 :
3114 0 : bool isPromise = promiseObj->is<PromiseObject>();
3115 0 : if (isPromise) {
3116 3658 : promise = &promiseObj->as<PromiseObject>();
3117 : } else {
3118 0 : RootedObject unwrappedPromiseObj(cx, CheckedUnwrap(promiseObj));
3119 1 : if (!unwrappedPromiseObj) {
3120 0 : ReportAccessDenied(cx);
3121 0 : return false;
3122 : }
3123 8 : if (!unwrappedPromiseObj->is<PromiseObject>()) {
3124 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
3125 0 : "Promise", "then", "value");
3126 0 : return false;
3127 : }
3128 8 : promise = &unwrappedPromiseObj->as<PromiseObject>();
3129 : }
3130 :
3131 : // Steps 3-5.
3132 : CreateDependentPromise createDependent = rvalUsed
3133 0 : ? CreateDependentPromise::Always
3134 0 : : CreateDependentPromise::SkipIfCtorUnobservable;
3135 0 : RootedObject resultPromise(cx);
3136 3666 : if (!OriginalPromiseThen(cx, promise, onFulfilled, onRejected, &resultPromise,
3137 : createDependent))
3138 : {
3139 : return false;
3140 : }
3141 :
3142 0 : if (rvalUsed)
3143 1701 : rval.setObject(*resultPromise);
3144 : else
3145 : rval.setUndefined();
3146 : return true;
3147 : }
3148 :
3149 : // ES2016, 25.4.5.3.
3150 : bool
3151 121 : Promise_then_noRetVal(JSContext* cx, unsigned argc, Value* vp)
3152 : {
3153 0 : CallArgs args = CallArgsFromVp(argc, vp);
3154 0 : return Promise_then_impl(cx, args.thisv(), args.get(0), args.get(1), args.rval(),
3155 242 : IsPromiseThenOrCatchRetValImplicitlyUsed(cx));
3156 : }
3157 :
3158 : // ES2016, 25.4.5.3.
3159 : static bool
3160 1514 : Promise_then(JSContext* cx, unsigned argc, Value* vp)
3161 : {
3162 0 : CallArgs args = CallArgsFromVp(argc, vp);
3163 6056 : return Promise_then_impl(cx, args.thisv(), args.get(0), args.get(1), args.rval(), true);
3164 : }
3165 :
3166 : // ES2016, 25.4.5.3.1.
3167 : static MOZ_MUST_USE bool
3168 2076 : PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled_,
3169 : HandleValue onRejected_, HandleObject resultPromise,
3170 : HandleObject resolve, HandleObject reject)
3171 : {
3172 : // Step 1 (implicit).
3173 : // Step 2 (implicit).
3174 :
3175 : // Step 3.
3176 0 : RootedValue onFulfilled(cx, onFulfilled_);
3177 0 : if (!IsCallable(onFulfilled))
3178 532 : onFulfilled = Int32Value(PromiseHandlerIdentity);
3179 :
3180 : // Step 4.
3181 0 : RootedValue onRejected(cx, onRejected_);
3182 0 : if (!IsCallable(onRejected))
3183 608 : onRejected = Int32Value(PromiseHandlerThrower);
3184 :
3185 0 : RootedObject incumbentGlobal(cx);
3186 2076 : if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
3187 : return false;
3188 :
3189 : // Step 7.
3190 8304 : Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise,
3191 : onFulfilled, onRejected,
3192 : resolve, reject,
3193 0 : incumbentGlobal));
3194 2076 : if (!reaction)
3195 : return false;
3196 :
3197 2076 : return PerformPromiseThenWithReaction(cx, promise, reaction);
3198 : }
3199 :
3200 : static MOZ_MUST_USE bool
3201 3143 : PerformPromiseThenWithReaction(JSContext* cx, Handle<PromiseObject*> promise,
3202 : Handle<PromiseReactionRecord*> reaction)
3203 : {
3204 0 : JS::PromiseState state = promise->state();
3205 0 : int32_t flags = promise->getFixedSlot(PromiseSlot_Flags).toInt32();
3206 3143 : if (state == JS::PromiseState::Pending) {
3207 : // Steps 5,6 (reordered).
3208 : // Instead of creating separate reaction records for fulfillment and
3209 : // rejection, we create a combined record. All places we use the record
3210 : // can handle that.
3211 2631 : if (!AddPromiseReaction(cx, promise, reaction))
3212 : return false;
3213 : }
3214 :
3215 : // Steps 8,9.
3216 : else {
3217 : // Step 9.a.
3218 512 : MOZ_ASSERT_IF(state != JS::PromiseState::Fulfilled, state == JS::PromiseState::Rejected);
3219 :
3220 : // Step 8.a. / 9.b.
3221 1024 : RootedValue valueOrReason(cx, promise->getFixedSlot(PromiseSlot_ReactionsOrResult));
3222 :
3223 : // We might be operating on a promise from another compartment. In
3224 : // that case, we need to wrap the result/reason value before using it.
3225 1 : if (!cx->compartment()->wrap(cx, &valueOrReason))
3226 0 : return false;
3227 :
3228 : // Step 9.c.
3229 0 : if (state == JS::PromiseState::Rejected && !(flags & PROMISE_FLAG_HANDLED))
3230 2 : cx->runtime()->removeUnhandledRejectedPromise(cx, promise);
3231 :
3232 : // Step 8.b. / 9.d.
3233 1024 : if (!EnqueuePromiseReactionJob(cx, reaction, valueOrReason, state))
3234 : return false;
3235 : }
3236 :
3237 : // Step 10.
3238 9429 : promise->setFixedSlot(PromiseSlot_Flags, Int32Value(flags | PROMISE_FLAG_HANDLED));
3239 :
3240 : // Step 11.
3241 3143 : return true;
3242 : }
3243 :
3244 : /**
3245 : * Calls |promise.then| with the provided hooks and adds |blockedPromise| to
3246 : * its list of dependent promises. Used by |Promise.all| and |Promise.race|.
3247 : *
3248 : * If |promise.then| is the original |Promise.prototype.then| function and
3249 : * the call to |promise.then| would use the original |Promise| constructor to
3250 : * create the resulting promise, this function skips the call to |promise.then|
3251 : * and thus creating a new promise that would not be observable by content.
3252 : */
3253 : static MOZ_MUST_USE bool
3254 242 : BlockOnPromise(JSContext* cx, HandleValue promiseVal, HandleObject blockedPromise_,
3255 : HandleValue onFulfilled, HandleValue onRejected)
3256 : {
3257 0 : RootedObject promiseObj(cx, ToObject(cx, promiseVal));
3258 242 : if (!promiseObj)
3259 : return false;
3260 :
3261 0 : RootedValue thenVal(cx);
3262 968 : if (!GetProperty(cx, promiseObj, promiseVal, cx->names().then, &thenVal))
3263 : return false;
3264 :
3265 726 : if (promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
3266 : // |promise| is an unwrapped Promise, and |then| is the original
3267 : // |Promise.prototype.then|, inline it here.
3268 : // 25.4.5.3., step 3.
3269 : RootedObject PromiseCtor(cx,
3270 0 : GlobalObject::getOrCreatePromiseConstructor(cx, cx->global()));
3271 0 : if (!PromiseCtor)
3272 242 : return false;
3273 :
3274 0 : RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
3275 0 : if (!C)
3276 242 : return false;
3277 :
3278 0 : RootedObject resultPromise(cx, blockedPromise_);
3279 0 : RootedObject resolveFun(cx);
3280 242 : RootedObject rejectFun(cx);
3281 :
3282 : // By default, the blocked promise is added as an extra entry to the
3283 : // rejected promises list.
3284 242 : bool addToDependent = true;
3285 :
3286 726 : if (C == PromiseCtor && resultPromise->is<PromiseObject>()) {
3287 : addToDependent = false;
3288 : } else {
3289 : // 25.4.5.3., step 4.
3290 0 : if (!NewPromiseCapability(cx, C, &resultPromise, &resolveFun, &rejectFun, true))
3291 242 : return false;
3292 : }
3293 :
3294 : // 25.4.5.3., step 5.
3295 0 : Rooted<PromiseObject*> promise(cx, &promiseObj->as<PromiseObject>());
3296 968 : if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise,
3297 : resolveFun, rejectFun))
3298 : {
3299 242 : return false;
3300 : }
3301 :
3302 242 : if (!addToDependent)
3303 : return true;
3304 : } else {
3305 : // Optimization failed, do the normal call.
3306 0 : RootedValue rval(cx);
3307 0 : if (!Call(cx, thenVal, promiseVal, onFulfilled, onRejected, &rval))
3308 0 : return false;
3309 : }
3310 :
3311 : // In case the value to depend on isn't an object at all, there's nothing
3312 : // more to do here: we can only add reactions to Promise objects
3313 : // (potentially after unwrapping them), and non-object values can't be
3314 : // Promise objects. This can happen if Promise.all is called on an object
3315 : // with a `resolve` method that returns primitives.
3316 0 : if (!promiseVal.isObject())
3317 : return true;
3318 :
3319 : // The object created by the |promise.then| call or the inlined version
3320 : // of it above is visible to content (either because |promise.then| was
3321 : // overridden by content and could leak it, or because a constructor
3322 : // other than the original value of |Promise| was used to create it).
3323 : // To have both that object and |blockedPromise| show up as dependent
3324 : // promises in the debugger, add a dummy reaction to the list of reject
3325 : // reactions that contains |blockedPromise|, but otherwise does nothing.
3326 0 : RootedObject unwrappedPromiseObj(cx, promiseObj);
3327 0 : RootedObject blockedPromise(cx, blockedPromise_);
3328 :
3329 0 : mozilla::Maybe<AutoRealm> ar;
3330 0 : if (IsProxy(promiseObj)) {
3331 0 : unwrappedPromiseObj = CheckedUnwrap(promiseObj);
3332 0 : if (!unwrappedPromiseObj) {
3333 0 : ReportAccessDenied(cx);
3334 0 : return false;
3335 : }
3336 0 : if (JS_IsDeadWrapper(unwrappedPromiseObj)) {
3337 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
3338 0 : return false;
3339 : }
3340 0 : ar.emplace(cx, unwrappedPromiseObj);
3341 0 : if (!cx->compartment()->wrap(cx, &blockedPromise))
3342 : return false;
3343 : }
3344 :
3345 : // If either the object to depend on or the object that gets blocked isn't
3346 : // a, maybe-wrapped, Promise instance, we ignore it. All this does is lose
3347 : // some small amount of debug information in scenarios that are highly
3348 : // unlikely to occur in useful code.
3349 0 : if (!unwrappedPromiseObj->is<PromiseObject>())
3350 : return true;
3351 0 : if (!blockedPromise_->is<PromiseObject>())
3352 : return true;
3353 :
3354 0 : Rooted<PromiseObject*> promise(cx, &unwrappedPromiseObj->as<PromiseObject>());
3355 0 : return AddPromiseReaction(cx, promise, UndefinedHandleValue, UndefinedHandleValue,
3356 0 : blockedPromise, nullptr, nullptr, nullptr);
3357 : }
3358 :
3359 : static MOZ_MUST_USE bool
3360 2631 : AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise,
3361 : Handle<PromiseReactionRecord*> reaction)
3362 : {
3363 0 : MOZ_RELEASE_ASSERT(reaction->is<PromiseReactionRecord>());
3364 7893 : RootedValue reactionVal(cx, ObjectValue(*reaction));
3365 :
3366 : // The code that creates Promise reactions can handle wrapped Promises,
3367 : // unwrapping them as needed. That means that the `promise` and `reaction`
3368 : // objects we have here aren't necessarily from the same compartment. In
3369 : // order to store the reaction on the promise, we have to ensure that it
3370 : // is properly wrapped.
3371 0 : mozilla::Maybe<AutoRealm> ar;
3372 0 : if (promise->compartment() != cx->compartment()) {
3373 0 : ar.emplace(cx, promise);
3374 6 : if (!cx->compartment()->wrap(cx, &reactionVal))
3375 : return false;
3376 : }
3377 :
3378 : // 25.4.5.3.1 steps 7.a,b.
3379 0 : RootedValue reactionsVal(cx, promise->getFixedSlot(PromiseSlot_ReactionsOrResult));
3380 5262 : RootedNativeObject reactions(cx);
3381 :
3382 2631 : if (reactionsVal.isUndefined()) {
3383 : // If no reactions existed so far, just store the reaction record directly.
3384 0 : promise->setFixedSlot(PromiseSlot_ReactionsOrResult, reactionVal);
3385 2494 : return true;
3386 : }
3387 :
3388 274 : RootedObject reactionsObj(cx, &reactionsVal.toObject());
3389 :
3390 : // If only a single reaction exists, it's stored directly instead of in a
3391 : // list. In that case, `reactionsObj` might be a wrapper, which we can
3392 : // always safely unwrap.
3393 0 : if (IsProxy(reactionsObj)) {
3394 0 : reactionsObj = UncheckedUnwrap(reactionsObj);
3395 0 : if (JS_IsDeadWrapper(reactionsObj)) {
3396 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
3397 0 : return false;
3398 : }
3399 0 : MOZ_RELEASE_ASSERT(reactionsObj->is<PromiseReactionRecord>());
3400 : }
3401 :
3402 274 : if (reactionsObj->is<PromiseReactionRecord>()) {
3403 : // If a single reaction existed so far, create a list and store the
3404 : // old and the new reaction in it.
3405 0 : reactions = NewDenseFullyAllocatedArray(cx, 2);
3406 134 : if (!reactions)
3407 : return false;
3408 268 : if (reactions->ensureDenseElements(cx, 0, 2) != DenseElementResult::Success)
3409 : return false;
3410 :
3411 0 : reactions->setDenseElement(0, reactionsVal);
3412 268 : reactions->setDenseElement(1, reactionVal);
3413 :
3414 402 : promise->setFixedSlot(PromiseSlot_ReactionsOrResult, ObjectValue(*reactions));
3415 : } else {
3416 : // Otherwise, just store the new reaction.
3417 0 : MOZ_RELEASE_ASSERT(reactionsObj->is<NativeObject>());
3418 0 : reactions = &reactionsObj->as<NativeObject>();
3419 0 : uint32_t len = reactions->getDenseInitializedLength();
3420 6 : if (reactions->ensureDenseElements(cx, 0, len + 1) != DenseElementResult::Success)
3421 : return false;
3422 6 : reactions->setDenseElement(len, reactionVal);
3423 : }
3424 :
3425 : return true;
3426 : }
3427 :
3428 : static MOZ_MUST_USE bool
3429 0 : AddPromiseReaction(JSContext* cx, Handle<PromiseObject*> promise, HandleValue onFulfilled,
3430 : HandleValue onRejected, HandleObject dependentPromise,
3431 : HandleObject resolve, HandleObject reject, HandleObject incumbentGlobal)
3432 : {
3433 0 : if (promise->state() != JS::PromiseState::Pending)
3434 : return true;
3435 :
3436 0 : Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, dependentPromise,
3437 : onFulfilled, onRejected,
3438 : resolve, reject,
3439 0 : incumbentGlobal));
3440 0 : if (!reaction)
3441 : return false;
3442 0 : return AddPromiseReaction(cx, promise, reaction);
3443 : }
3444 :
3445 : uint64_t
3446 0 : PromiseObject::getID()
3447 : {
3448 0 : return PromiseDebugInfo::id(this);
3449 : }
3450 :
3451 : double
3452 0 : PromiseObject::lifetime()
3453 : {
3454 0 : return MillisecondsSinceStartup() - allocationTime();
3455 : }
3456 :
3457 : /**
3458 : * Returns all promises that directly depend on this one. That means those
3459 : * created by calling `then` on this promise, or the promise returned by
3460 : * `Promise.all(iterable)` or `Promise.race(iterable)`, with this promise
3461 : * being a member of the passed-in `iterable`.
3462 : *
3463 : * Per spec, we should have separate lists of reaction records for the
3464 : * fulfill and reject cases. As an optimization, we have only one of those,
3465 : * containing the required data for both cases. So we just walk that list
3466 : * and extract the dependent promises from all reaction records.
3467 : */
3468 : bool
3469 0 : PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> values)
3470 : {
3471 0 : if (state() != JS::PromiseState::Pending)
3472 : return true;
3473 :
3474 0 : RootedValue reactionsVal(cx, getFixedSlot(PromiseSlot_ReactionsOrResult));
3475 :
3476 : // If no reactions are pending, we don't have list and are done.
3477 0 : if (reactionsVal.isNullOrUndefined())
3478 : return true;
3479 :
3480 0 : RootedNativeObject reactions(cx, &reactionsVal.toObject().as<NativeObject>());
3481 :
3482 : // If only a single reaction is pending, it's stored directly.
3483 0 : if (reactions->is<PromiseReactionRecord>()) {
3484 : // Not all reactions have a Promise on them.
3485 0 : RootedObject promiseObj(cx, reactions->as<PromiseReactionRecord>().promise());
3486 0 : if (!promiseObj)
3487 : return true;
3488 :
3489 0 : if (!values.growBy(1))
3490 : return false;
3491 :
3492 0 : values[0].setObject(*promiseObj);
3493 0 : return true;
3494 : }
3495 :
3496 0 : uint32_t len = reactions->getDenseInitializedLength();
3497 0 : MOZ_ASSERT(len >= 2);
3498 :
3499 0 : size_t valuesIndex = 0;
3500 0 : Rooted<PromiseReactionRecord*> reaction(cx);
3501 0 : for (size_t i = 0; i < len; i++) {
3502 0 : reaction = &reactions->getDenseElement(i).toObject().as<PromiseReactionRecord>();
3503 :
3504 : // Not all reactions have a Promise on them.
3505 0 : RootedObject promiseObj(cx, reaction->promise());
3506 0 : if (!promiseObj)
3507 0 : continue;
3508 0 : if (!values.growBy(1))
3509 0 : return false;
3510 :
3511 0 : values[valuesIndex++].setObject(*promiseObj);
3512 : }
3513 :
3514 : return true;
3515 : }
3516 :
3517 : /* static */ bool
3518 117 : PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValue resolutionValue)
3519 : {
3520 0 : MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
3521 117 : if (promise->state() != JS::PromiseState::Pending)
3522 : return true;
3523 :
3524 0 : if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION))
3525 97 : return ResolvePromiseInternal(cx, promise, resolutionValue);
3526 :
3527 0 : RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise));
3528 0 : RootedValue funVal(cx, ObjectValue(*resolveFun));
3529 :
3530 : // For xray'd Promises, the resolve fun may have been created in another
3531 : // compartment. For the call below to work in that case, wrap the
3532 : // function into the current compartment.
3533 0 : if (!cx->compartment()->wrap(cx, &funVal))
3534 : return false;
3535 :
3536 0 : FixedInvokeArgs<1> args(cx);
3537 0 : args[0].set(resolutionValue);
3538 :
3539 0 : RootedValue dummy(cx);
3540 0 : return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
3541 : }
3542 :
3543 : /* static */ bool
3544 0 : PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue)
3545 : {
3546 0 : MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
3547 0 : if (promise->state() != JS::PromiseState::Pending)
3548 : return true;
3549 :
3550 0 : if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION))
3551 0 : return ResolvePromise(cx, promise, rejectionValue, JS::PromiseState::Rejected);
3552 :
3553 0 : RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction));
3554 0 : MOZ_ASSERT(IsCallable(funVal));
3555 :
3556 0 : FixedInvokeArgs<1> args(cx);
3557 0 : args[0].set(rejectionValue);
3558 :
3559 0 : RootedValue dummy(cx);
3560 0 : return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
3561 : }
3562 :
3563 : /* static */ void
3564 4103 : PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise)
3565 : {
3566 4103 : PromiseDebugInfo::setResolutionInfo(cx, promise);
3567 :
3568 0 : if (promise->state() == JS::PromiseState::Rejected && promise->isUnhandled())
3569 4 : cx->runtime()->addUnhandledRejectedPromise(cx, promise);
3570 :
3571 0 : Debugger::onPromiseSettled(cx, promise);
3572 4103 : }
3573 :
3574 0 : OffThreadPromiseTask::OffThreadPromiseTask(JSContext* cx, Handle<PromiseObject*> promise)
3575 0 : : runtime_(cx->runtime()),
3576 : promise_(cx, promise),
3577 0 : registered_(false)
3578 : {
3579 0 : MOZ_ASSERT(runtime_ == promise_->zone()->runtimeFromMainThread());
3580 0 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
3581 0 : MOZ_ASSERT(cx->runtime()->offThreadPromiseState.ref().initialized());
3582 0 : }
3583 :
3584 0 : OffThreadPromiseTask::~OffThreadPromiseTask()
3585 : {
3586 0 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
3587 :
3588 0 : OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
3589 0 : MOZ_ASSERT(state.initialized());
3590 :
3591 0 : if (registered_) {
3592 0 : LockGuard<Mutex> lock(state.mutex_);
3593 0 : state.live_.remove(this);
3594 : }
3595 0 : }
3596 :
3597 : bool
3598 0 : OffThreadPromiseTask::init(JSContext* cx)
3599 : {
3600 0 : MOZ_ASSERT(cx->runtime() == runtime_);
3601 0 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
3602 :
3603 0 : OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
3604 0 : MOZ_ASSERT(state.initialized());
3605 :
3606 0 : LockGuard<Mutex> lock(state.mutex_);
3607 :
3608 0 : if (!state.live_.putNew(this)) {
3609 0 : ReportOutOfMemory(cx);
3610 0 : return false;
3611 : }
3612 :
3613 0 : registered_ = true;
3614 0 : return true;
3615 : }
3616 :
3617 : void
3618 0 : OffThreadPromiseTask::run(JSContext* cx, MaybeShuttingDown maybeShuttingDown)
3619 : {
3620 0 : MOZ_ASSERT(cx->runtime() == runtime_);
3621 0 : MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime_));
3622 0 : MOZ_ASSERT(registered_);
3623 0 : MOZ_ASSERT(runtime_->offThreadPromiseState.ref().initialized());
3624 :
3625 0 : if (maybeShuttingDown == JS::Dispatchable::NotShuttingDown) {
3626 : // We can't leave a pending exception when returning to the caller so do
3627 : // the same thing as Gecko, which is to ignore the error. This should
3628 : // only happen due to OOM or interruption.
3629 0 : AutoRealm ar(cx, promise_);
3630 0 : if (!resolve(cx, promise_))
3631 0 : cx->clearPendingException();
3632 : }
3633 :
3634 0 : js_delete(this);
3635 0 : }
3636 :
3637 : void
3638 0 : OffThreadPromiseTask::dispatchResolveAndDestroy()
3639 : {
3640 0 : MOZ_ASSERT(registered_);
3641 :
3642 0 : OffThreadPromiseRuntimeState& state = runtime_->offThreadPromiseState.ref();
3643 0 : MOZ_ASSERT(state.initialized());
3644 0 : MOZ_ASSERT((LockGuard<Mutex>(state.mutex_), state.live_.has(this)));
3645 :
3646 : // If the dispatch succeeds, then we are guaranteed that run() will be
3647 : // called on an active JSContext of runtime_.
3648 0 : if (state.dispatchToEventLoopCallback_(state.dispatchToEventLoopClosure_, this))
3649 : return;
3650 :
3651 : // We assume, by interface contract, that if the dispatch fails, it's
3652 : // because the embedding is in the process of shutting down the JSRuntime.
3653 : // Since JSRuntime destruction calls shutdown(), we can rely on shutdown()
3654 : // to delete the task on its active JSContext thread. shutdown() waits for
3655 : // numCanceled_ == live_.length, so we notify when this condition is
3656 : // reached.
3657 0 : LockGuard<Mutex> lock(state.mutex_);
3658 0 : state.numCanceled_++;
3659 0 : if (state.numCanceled_ == state.live_.count())
3660 0 : state.allCanceled_.notify_one();
3661 : }
3662 :
3663 4 : OffThreadPromiseRuntimeState::OffThreadPromiseRuntimeState()
3664 : : dispatchToEventLoopCallback_(nullptr),
3665 : dispatchToEventLoopClosure_(nullptr),
3666 : mutex_(mutexid::OffThreadPromiseState),
3667 : numCanceled_(0),
3668 20 : internalDispatchQueueClosed_(false)
3669 : {
3670 0 : AutoEnterOOMUnsafeRegion noOOM;
3671 1 : if (!live_.init())
3672 0 : noOOM.crash("OffThreadPromiseRuntimeState");
3673 4 : }
3674 :
3675 0 : OffThreadPromiseRuntimeState::~OffThreadPromiseRuntimeState()
3676 : {
3677 0 : MOZ_ASSERT(live_.empty());
3678 0 : MOZ_ASSERT(numCanceled_ == 0);
3679 0 : MOZ_ASSERT(internalDispatchQueue_.empty());
3680 0 : MOZ_ASSERT(!initialized());
3681 0 : }
3682 :
3683 : void
3684 4 : OffThreadPromiseRuntimeState::init(JS::DispatchToEventLoopCallback callback, void* closure)
3685 : {
3686 4 : MOZ_ASSERT(!initialized());
3687 :
3688 0 : dispatchToEventLoopCallback_ = callback;
3689 4 : dispatchToEventLoopClosure_ = closure;
3690 :
3691 0 : MOZ_ASSERT(initialized());
3692 4 : }
3693 :
3694 : /* static */ bool
3695 0 : OffThreadPromiseRuntimeState::internalDispatchToEventLoop(void* closure, JS::Dispatchable* d)
3696 : {
3697 0 : OffThreadPromiseRuntimeState& state = *reinterpret_cast<OffThreadPromiseRuntimeState*>(closure);
3698 0 : MOZ_ASSERT(state.usingInternalDispatchQueue());
3699 :
3700 0 : LockGuard<Mutex> lock(state.mutex_);
3701 :
3702 0 : if (state.internalDispatchQueueClosed_)
3703 : return false;
3704 :
3705 : // The JS API contract is that 'false' means shutdown, so be infallible
3706 : // here (like Gecko).
3707 0 : AutoEnterOOMUnsafeRegion noOOM;
3708 0 : if (!state.internalDispatchQueue_.append(d))
3709 0 : noOOM.crash("internalDispatchToEventLoop");
3710 :
3711 : // Wake up internalDrain() if it is waiting for a job to finish.
3712 0 : state.internalDispatchQueueAppended_.notify_one();
3713 : return true;
3714 : }
3715 :
3716 : bool
3717 0 : OffThreadPromiseRuntimeState::usingInternalDispatchQueue() const
3718 : {
3719 0 : return dispatchToEventLoopCallback_ == internalDispatchToEventLoop;
3720 : }
3721 :
3722 : void
3723 0 : OffThreadPromiseRuntimeState::initInternalDispatchQueue()
3724 : {
3725 0 : init(internalDispatchToEventLoop, this);
3726 0 : MOZ_ASSERT(usingInternalDispatchQueue());
3727 0 : }
3728 :
3729 : bool
3730 0 : OffThreadPromiseRuntimeState::initialized() const
3731 : {
3732 4 : return !!dispatchToEventLoopCallback_;
3733 : }
3734 :
3735 : void
3736 0 : OffThreadPromiseRuntimeState::internalDrain(JSContext* cx)
3737 : {
3738 0 : MOZ_ASSERT(usingInternalDispatchQueue());
3739 0 : MOZ_ASSERT(!internalDispatchQueueClosed_);
3740 :
3741 : while (true) {
3742 0 : DispatchableVector dispatchQueue;
3743 : {
3744 0 : LockGuard<Mutex> lock(mutex_);
3745 :
3746 0 : MOZ_ASSERT_IF(!internalDispatchQueue_.empty(), !live_.empty());
3747 0 : if (live_.empty())
3748 0 : return;
3749 :
3750 0 : while (internalDispatchQueue_.empty())
3751 0 : internalDispatchQueueAppended_.wait(lock);
3752 :
3753 0 : Swap(dispatchQueue, internalDispatchQueue_);
3754 0 : MOZ_ASSERT(internalDispatchQueue_.empty());
3755 : }
3756 :
3757 : // Don't call run() with mutex_ held to avoid deadlock.
3758 0 : for (JS::Dispatchable* d : dispatchQueue)
3759 0 : d->run(cx, JS::Dispatchable::NotShuttingDown);
3760 : }
3761 : }
3762 :
3763 : bool
3764 0 : OffThreadPromiseRuntimeState::internalHasPending()
3765 : {
3766 0 : MOZ_ASSERT(usingInternalDispatchQueue());
3767 0 : MOZ_ASSERT(!internalDispatchQueueClosed_);
3768 :
3769 0 : LockGuard<Mutex> lock(mutex_);
3770 0 : MOZ_ASSERT_IF(!internalDispatchQueue_.empty(), !live_.empty());
3771 0 : return !live_.empty();
3772 : }
3773 :
3774 : void
3775 0 : OffThreadPromiseRuntimeState::shutdown(JSContext* cx)
3776 : {
3777 0 : if (!initialized())
3778 : return;
3779 :
3780 : // When the shell is using the internal event loop, we must simulate our
3781 : // requirement of the embedding that, before shutdown, all successfully-
3782 : // dispatched-to-event-loop tasks have been run.
3783 0 : if (usingInternalDispatchQueue()) {
3784 0 : DispatchableVector dispatchQueue;
3785 : {
3786 0 : LockGuard<Mutex> lock(mutex_);
3787 0 : Swap(dispatchQueue, internalDispatchQueue_);
3788 0 : MOZ_ASSERT(internalDispatchQueue_.empty());
3789 0 : internalDispatchQueueClosed_ = true;
3790 : }
3791 :
3792 : // Don't call run() with mutex_ held to avoid deadlock.
3793 0 : for (JS::Dispatchable* d : dispatchQueue)
3794 0 : d->run(cx, JS::Dispatchable::ShuttingDown);
3795 : }
3796 :
3797 : {
3798 : // Wait until all live OffThreadPromiseRuntimeState have been confirmed
3799 : // canceled by OffThreadPromiseTask::dispatchResolve().
3800 0 : LockGuard<Mutex> lock(mutex_);
3801 0 : while (live_.count() != numCanceled_) {
3802 0 : MOZ_ASSERT(numCanceled_ < live_.count());
3803 0 : allCanceled_.wait(lock);
3804 : }
3805 : }
3806 :
3807 : // Now that all the tasks have stopped concurrent execution, we can just
3808 : // delete everything. We don't want each OffThreadPromiseTask to unregister
3809 : // itself (which would mutate live_ while we are iterating over it) so reset
3810 : // the tasks' internal registered_ flag.
3811 0 : for (OffThreadPromiseTaskSet::Range r = live_.all(); !r.empty(); r.popFront()) {
3812 0 : OffThreadPromiseTask* task = r.front();
3813 0 : MOZ_ASSERT(task->registered_);
3814 0 : task->registered_ = false;
3815 0 : js_delete(task);
3816 : }
3817 0 : live_.clear();
3818 0 : numCanceled_ = 0;
3819 :
3820 : // After shutdown, there should be no OffThreadPromiseTask activity in this
3821 : // JSRuntime. Revert to the !initialized() state to catch bugs.
3822 0 : dispatchToEventLoopCallback_ = nullptr;
3823 0 : MOZ_ASSERT(!initialized());
3824 : }
3825 :
3826 : static JSObject*
3827 22 : CreatePromisePrototype(JSContext* cx, JSProtoKey key)
3828 : {
3829 : return GlobalObject::createBlankPrototype(cx, cx->global(), &PromiseObject::protoClass_);
3830 : }
3831 :
3832 : const JSJitInfo promise_then_info = {
3833 : { (JSJitGetterOp)Promise_then_noRetVal },
3834 : { 0 }, /* unused */
3835 : { 0 }, /* unused */
3836 : JSJitInfo::IgnoresReturnValueNative,
3837 : JSJitInfo::AliasEverything,
3838 : JSVAL_TYPE_UNDEFINED,
3839 : };
3840 :
3841 : const JSJitInfo promise_catch_info = {
3842 : { (JSJitGetterOp)Promise_catch_noRetVal },
3843 : { 0 }, /* unused */
3844 : { 0 }, /* unused */
3845 : JSJitInfo::IgnoresReturnValueNative,
3846 : JSJitInfo::AliasEverything,
3847 : JSVAL_TYPE_UNDEFINED,
3848 : };
3849 :
3850 : static const JSFunctionSpec promise_methods[] = {
3851 : JS_FNINFO("then", Promise_then, &promise_then_info, 2, 0),
3852 : JS_FNINFO("catch", Promise_catch, &promise_catch_info, 1, 0),
3853 : JS_SELF_HOSTED_FN("finally", "Promise_finally", 1, 0),
3854 : JS_FS_END
3855 : };
3856 :
3857 : static const JSPropertySpec promise_properties[] = {
3858 : JS_STRING_SYM_PS(toStringTag, "Promise", JSPROP_READONLY),
3859 : JS_PS_END
3860 : };
3861 :
3862 : static const JSFunctionSpec promise_static_methods[] = {
3863 : JS_FN("all", Promise_static_all, 1, 0),
3864 : JS_FN("race", Promise_static_race, 1, 0),
3865 : JS_FN("reject", Promise_reject, 1, 0),
3866 : JS_FN("resolve", Promise_static_resolve, 1, 0),
3867 : JS_FS_END
3868 : };
3869 :
3870 : static const JSPropertySpec promise_static_properties[] = {
3871 : JS_SYM_GET(species, Promise_static_species, 0),
3872 : JS_PS_END
3873 : };
3874 :
3875 : static const ClassSpec PromiseObjectClassSpec = {
3876 : GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
3877 : CreatePromisePrototype,
3878 : promise_static_methods,
3879 : promise_static_properties,
3880 : promise_methods,
3881 : promise_properties
3882 : };
3883 :
3884 : const Class PromiseObject::class_ = {
3885 : "Promise",
3886 : JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) |
3887 : JSCLASS_HAS_XRAYED_CONSTRUCTOR,
3888 : JS_NULL_CLASS_OPS,
3889 : &PromiseObjectClassSpec
3890 : };
3891 :
3892 : const Class PromiseObject::protoClass_ = {
3893 : "PromiseProto",
3894 : JSCLASS_HAS_CACHED_PROTO(JSProto_Promise),
3895 : JS_NULL_CLASS_OPS,
3896 : &PromiseObjectClassSpec
3897 : };
|