From 38c1d558afd03d61bd9932032f47d9c36f177b08 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 10:30:54 -0500 Subject: Bug 1379525 - Part 1: Await on the value before yielding or returning inside async generator. Tag #1287 --- js/src/builtin/Promise.cpp | 387 ++++++++++++++++++++++-------------- js/src/frontend/BytecodeEmitter.cpp | 30 ++- js/src/vm/AsyncIteration.cpp | 151 ++++++-------- js/src/vm/AsyncIteration.h | 34 +++- 4 files changed, 344 insertions(+), 258 deletions(-) (limited to 'js/src') diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 6bb2d61d7..62c5eb429 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -36,18 +36,30 @@ MillisecondsSinceStartup() enum PromiseHandler { PromiseHandlerIdentity = 0, PromiseHandlerThrower, - PromiseHandlerAsyncFunctionAwaitFulfilled, - PromiseHandlerAsyncFunctionAwaitRejected, - PromiseHandlerAsyncGeneratorAwaitFulfilled, - PromiseHandlerAsyncGeneratorAwaitRejected, - - // Async Iteration proposal 6.1.1.2.1. - // Async iterator handlers take the resolved value and create new iterator - // objects. To do so it needs to forward whether the iterator is done. In - // spec, this is achieved via the [[Done]] internal slot. We enumerate both - // true and false cases here. - PromiseHandlerAsyncIteratorValueUnwrapDone, - PromiseHandlerAsyncIteratorValueUnwrapNotDone, + + // ES 2018 draft 25.5.5.4-5. + PromiseHandlerAsyncFunctionAwaitedFulfilled, + PromiseHandlerAsyncFunctionAwaitedRejected, + + // Async Iteration proposal 4.1. + PromiseHandlerAsyncGeneratorAwaitedFulfilled, + PromiseHandlerAsyncGeneratorAwaitedRejected, + + // Async Iteration proposal 11.4.3.5.1-2. + PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled, + PromiseHandlerAsyncGeneratorResumeNextReturnRejected, + + // Async Iteration proposal 11.4.3.7 steps 8.c-e. + PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled, + PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected, + + // Async Iteration proposal 11.1.3.2.5. + // Async-from-Sync iterator handlers take the resolved value and create new + // iterator objects. To do so it needs to forward whether the iterator is + // done. In spec, this is achieved via the [[Done]] internal slot. We + // enumerate both true and false cases here. + PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone, + PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone, }; enum ResolutionMode { @@ -197,8 +209,8 @@ enum ReactionRecordSlots { #define REACTION_FLAG_RESOLVED 0x1 #define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 -#define REACTION_FLAG_ASYNC_FUNCTION_AWAIT 0x8 -#define REACTION_FLAG_ASYNC_GENERATOR_AWAIT 0x10 +#define REACTION_FLAG_ASYNC_FUNCTION 0x8 +#define REACTION_FLAG_ASYNC_GENERATOR 0x10 // ES2016, 25.4.1.2. class PromiseReactionRecord : public NativeObject @@ -225,28 +237,28 @@ class PromiseReactionRecord : public NativeObject flags |= REACTION_FLAG_FULFILLED; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - void setIsAsyncFunctionAwait() { + void setIsAsyncFunction() { int32_t flags = this->flags(); - flags |= REACTION_FLAG_ASYNC_FUNCTION_AWAIT; + flags |= REACTION_FLAG_ASYNC_FUNCTION; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - bool isAsyncFunctionAwait() { + bool isAsyncFunction() { int32_t flags = this->flags(); - return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT; + return flags & REACTION_FLAG_ASYNC_FUNCTION; } - void setIsAsyncGeneratorAwait(Handle asyncGenObj) { + void setIsAsyncGenerator(Handle asyncGenObj) { int32_t flags = this->flags(); - flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + flags |= REACTION_FLAG_ASYNC_GENERATOR; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj)); } - bool isAsyncGeneratorAwait() { + bool isAsyncGenerator() { int32_t flags = this->flags(); - return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + return flags & REACTION_FLAG_ASYNC_GENERATOR; } AsyncGeneratorObject* asyncGenerator() { - MOZ_ASSERT(isAsyncGeneratorAwait()); + MOZ_ASSERT(isAsyncGenerator()); return &getFixedSlot(ReactionRecordSlot_Generator).toObject() .as(); } @@ -848,10 +860,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat } static MOZ_MUST_USE bool -AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, - MutableHandleValue rval) +AsyncFunctionPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAsyncFunctionAwait()); + MOZ_ASSERT(reaction->isAsyncFunction()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); @@ -859,15 +871,14 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, HandlegetFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled || - handlerNum == PromiseHandlerAsyncFunctionAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled) { + if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected); if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument)) return false; } @@ -877,27 +888,48 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, - MutableHandleValue rval) +AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAsyncGeneratorAwait()); + MOZ_ASSERT(reaction->isAsyncGenerator()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); Rooted asyncGenObj(cx, reaction->asyncGenerator()); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled || - handlerNum == PromiseHandlerAsyncGeneratorAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled) { + if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) { + // 4.1.1. if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument)) return false; - } else { + } else if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedRejected) { + // 4.1.2. if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument)) return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled) { + asyncGenObj->setCompleted(); + // 11.4.3.5.1 step 1. + if (!AsyncGeneratorResolve(cx, asyncGenObj, argument, true)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnRejected) { + asyncGenObj->setCompleted(); + // 11.4.3.5.2 step 1. + if (!AsyncGeneratorReject(cx, asyncGenObj, argument)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled) { + asyncGenObj->setExecuting(); + // 11.4.3.7 steps 8.d-e. + if (!AsyncGeneratorYieldReturnAwaitedFulfilled(cx, asyncGenObj, argument)) + return false; + } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected); + asyncGenObj->setExecuting(); + // 11.4.3.7 step 8.c. + if (!AsyncGeneratorYieldReturnAwaitedRejected(cx, asyncGenObj, argument)) + return false; } rval.setUndefined(); @@ -947,10 +979,10 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) // Steps 1-2. Rooted reaction(cx, &reactionObj->as()); - if (reaction->isAsyncFunctionAwait()) - return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval()); - if (reaction->isAsyncGeneratorAwait()) - return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncFunction()) + return AsyncFunctionPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncGenerator()) + return AsyncGeneratorPromiseReactionJob(cx, reaction, args.rval()); // Step 3. RootedValue handlerVal(cx, reaction->handler()); @@ -972,11 +1004,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) resolutionMode = RejectMode; handlerResult = argument; } else { - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone || - handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone); + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone || + handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone); - bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone; - // Async Iteration proposal 6.1.1.2.1 step 1. + bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone; + // Async Iteration proposal 11.1.3.2.5 step 1. RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); if (!resultObj) return false; @@ -2214,7 +2246,7 @@ static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx, // Some async/await functions are implemented here instead of // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions. -// Async Functions proposal 1.1.8 and 1.2.14 step 1. +// ES 2018 draft 14.6.11 and 14.7.14 step 1. MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) { @@ -2228,7 +2260,7 @@ js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) return promise; } -// Async Functions proposal 2.2 steps 3.f, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.f, 3.g. MOZ_MUST_USE bool js::AsyncFunctionThrown(JSContext* cx, Handle resultPromise) { @@ -2244,7 +2276,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle resultPromise) return true; } -// Async Functions proposal 2.2 steps 3.d-e, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.d-e, 3.g. MOZ_MUST_USE bool js::AsyncFunctionReturned(JSContext* cx, Handle resultPromise, HandleValue value) { @@ -2256,28 +2288,27 @@ js::AsyncFunctionReturned(JSContext* cx, Handle resultPromise, H return true; } -// Async Functions proposal 2.3 steps 2-8. -MOZ_MUST_USE bool -js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value) +// Helper function that performs the equivalent steps as +// Async Iteration proposal 4.1 Await steps 2-3, 6-9 or similar. +template +static MOZ_MUST_USE bool +InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise, + HandleValue onFulfilled, HandleValue onRejected, T extraStep) { // Step 2. Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; - // Steps 3. + // Step 3. if (!ResolvePromiseInternal(cx, promise, value)) return false; - // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitFulfilled)); - RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitRejected)); - RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) return false; - // Steps 6-7. + // Steps 7-8. Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, onRejected, nullptr, nullptr, @@ -2285,53 +2316,47 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand if (!reaction) return false; - reaction->setIsAsyncFunctionAwait(); + // Step 6. + extraStep(reaction); - // Step 8. + // Step 9. return PerformPromiseThenWithReaction(cx, promise, reaction); } -// Async Iteration proposal 5.1 steps 2-9. +// ES 2018 draft 25.5.5.3 steps 2-10. MOZ_MUST_USE bool -js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, - HandleValue value) +js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value) { - // Step 2. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) - return false; - - // Steps 3. - if (!ResolvePromiseInternal(cx, promise, value)) - return false; - // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitFulfilled)); - RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitRejected)); - - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) - return false; + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected)); - // Step 6 (skipped). - - // Steps 7-8. - Rooted reaction(cx, NewReactionRecord(cx, nullptr, - onFulfilled, onRejected, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; + // Steps 2-3, 6-10. + auto extra = [](Handle reaction) { + reaction->setIsAsyncFunction(); + }; + return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra); +} - reaction->setIsAsyncGeneratorAwait(asyncGenObj); +// Async Iteration proposal 4.1 Await steps 2-9. +MOZ_MUST_USE bool +js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, + HandleValue value) +{ + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected)); - // Step 9. - return PerformPromiseThenWithReaction(cx, promise, reaction); + // Steps 2-3, 6-9. + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); } -// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next -// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return -// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw bool js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) { @@ -2368,11 +2393,11 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co RootedValue resultVal(cx); RootedValue func(cx); if (completionKind == CompletionKind::Normal) { - // 6.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.1 steps 5-6 (partially). if (!GetProperty(cx, iter, iter, cx->names().next, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); } else if (completionKind == CompletionKind::Return) { - // 6.1.3.2.2 steps 5-6. + // 11.1.3.2.2 steps 5-6. if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); @@ -2394,7 +2419,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co return true; } } else { - // 6.1.3.2.3 steps 5-6. + // 11.1.3.2.3 steps 5-6. MOZ_ASSERT(completionKind == CompletionKind::Throw); if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); @@ -2411,16 +2436,16 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co } } - // 6.1.3.2.1 steps 5-6 (partially). - // 6.1.3.2.2, 6.1.3.2.3 steps 8-9. + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 8-9. RootedValue iterVal(cx, ObjectValue(*iter)); FixedInvokeArgs<1> args2(cx); args2[0].set(args.get(0)); if (!js::Call(cx, func, iterVal, args2, &resultVal)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); - // 6.1.3.2.1 steps 5-6 (partially). - // 6.1.3.2.2, 6.1.3.2.3 steps 10. + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 10. if (!resultVal.isObject()) { CheckIsObjectKind kind; switch (completionKind) { @@ -2440,8 +2465,8 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co RootedObject resultObj(cx, &resultVal.toObject()); - // Following step numbers are for 6.1.3.2.1. - // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20. + // Following step numbers are for 11.1.3.2.1. + // For 11.1.3.2.2 and 11.1.3.2.3, steps 7-16 corresponds to steps 11-20. // Steps 7-8. RootedValue doneVal(cx); @@ -2454,33 +2479,15 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); - // Step 11. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) - return false; - - // Step 12. - if (!ResolvePromiseInternal(cx, promise, value)) - return false; - // Steps 13-14. RootedValue onFulfilled(cx, Int32Value(done - ? PromiseHandlerAsyncIteratorValueUnwrapDone - : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); + ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone + : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone)); - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) - return false; - - // Step 15. - Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, - UndefinedHandleValue, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; - - if (!PerformPromiseThenWithReaction(cx, promise, reaction)) + // Steps 11-12, 15. + auto extra = [](Handle reaction) { + }; + if (!InternalAwait(cx, value, resultPromise, onFulfilled, UndefinedHandleValue, extra)) return false; // Step 16. @@ -2488,7 +2495,10 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co return true; } -// Async Iteration proposal 6.4.3.3. +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); + +// Async Iteration proposal 11.4.3.3. MOZ_MUST_USE bool js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenObj, HandleValue value, bool done) @@ -2508,43 +2518,25 @@ js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenO RootedObject resultPromise(cx, request->promise()); // Step 6. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) - return false; - - // Step 7. - if (!ResolvePromiseInternal(cx, promise, value)) - return false; - - // Steps 8-9. - RootedValue onFulfilled(cx, Int32Value(done - ? PromiseHandlerAsyncIteratorValueUnwrapDone - : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); - - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) + RootedObject resultObj(cx, CreateIterResultObject(cx, value, done)); + if (!resultObj) return false; - // Step 10. - Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, - UndefinedHandleValue, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; + RootedValue resultValue(cx, ObjectValue(*resultObj)); - if (!PerformPromiseThenWithReaction(cx, promise, reaction)) + // Step 7. + if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) return false; - // Step 11. + // Step 8. if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) return false; - // Step 12. + // Step 9. return true; } -// Async Iteration proposal 6.4.3.4. +// Async Iteration proposal 11.4.3.4. MOZ_MUST_USE bool js::AsyncGeneratorReject(JSContext* cx, Handle asyncGenObj, HandleValue exception) @@ -2575,7 +2567,98 @@ js::AsyncGeneratorReject(JSContext* cx, Handle asyncGenOb return true; } -// Async Iteration proposal 6.4.3.6. +// Async Iteration proposal 11.4.3.5. +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isExecuting()); + + // Step 4. + if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn()) + return true; + + // Steps 5-6. + if (asyncGenObj->isQueueEmpty()) + return true; + + // Steps 7-8. + Rooted request( + cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 9. + CompletionKind completionKind = request->completionKind(); + + // Step 10. + if (completionKind != CompletionKind::Normal) { + // Step 10.a. + if (asyncGenObj->isSuspendedStart()) + asyncGenObj->setCompleted(); + + // Step 10.b. + if (asyncGenObj->isCompleted()) { + RootedValue value(cx, request->completionValue()); + + // Step 10.b.i. + if (completionKind == CompletionKind::Return) { + // Steps 10.b.i.1. + asyncGenObj->setAwaitingReturn(); + + // Steps 10.b.i.4-6 (reordered). + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnRejected)); + + // Steps 10.b.i.2-3, 7-10. + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); + } + + // Step 10.b.ii.1. + MOZ_ASSERT(completionKind == CompletionKind::Throw); + + // Steps 10.b.ii.2-3. + return AsyncGeneratorReject(cx, asyncGenObj, value); + } + } else if (asyncGenObj->isCompleted()) { + // Step 11. + return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); + } + + // Step 12. + MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); + + // Step 16 (reordered). + asyncGenObj->setExecuting(); + + RootedValue argument(cx, request->completionValue()); + + if (completionKind == CompletionKind::Return) { + // 11.4.3.7 AsyncGeneratorYield step 8.b-e. + // Since we don't have the place that handles return from yield + // inside the generator, handle the case here, with extra state + // State_AwaitingYieldReturn. + asyncGenObj->setAwaitingYieldReturn(); + + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected)); + + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra); + } + + // Steps 13-15, 17-21. + return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); +} + +// Async Iteration proposal 11.4.3.6. MOZ_MUST_USE bool js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind, HandleValue completionValue, diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f629c86e5..4710ab8e6 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8578,6 +8578,13 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (ParseNode* pn2 = pn->pn_kid) { if (!emitTree(pn2)) return false; + + bool isAsyncGenerator = sc->asFunctionBox()->isAsync() && + sc->asFunctionBox()->isStarGenerator(); + if (isAsyncGenerator) { + if (!emitAwait()) + return false; + } } else { /* No explicit return value provided */ if (!emit1(JSOP_UNDEFINED)) @@ -8690,6 +8697,14 @@ BytecodeEmitter::emitYield(ParseNode* pn) if (!emit1(JSOP_UNDEFINED)) return false; } + + // 11.4.3.7 AsyncGeneratorYield step 5. + bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); + if (isAsyncGenerator) { + if (!emitAwait()) // RESULT + return false; + } + if (needsIteratorResult) { if (!emitFinishIteratorResult(false)) return false; @@ -8762,6 +8777,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) MOZ_ASSERT(this->stackDepth == startDepth); + // 11.4.3.7 AsyncGeneratorYield step 5. + if (isAsyncGenerator) { + if (!emitAwait()) // ITER RESULT + return false; + } + // Load the generator object. if (!emitGetDotGenerator()) // ITER RESULT GENOBJ return false; @@ -8910,10 +8931,6 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) return false; if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE return false; - if (isAsyncGenerator) { - if (!emitAwait()) // ITER OLDRESULT FTYPE FVALUE VALUE - return false; - } if (!emitPrepareIteratorResult()) // ITER OLDRESULT FTYPE FVALUE VALUE RESULT return false; if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RESULT VALUE @@ -9006,11 +9023,6 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // VALUE return false; - if (isAsyncGenerator) { - if (!emitAwait()) // VALUE - return false; - } - MOZ_ASSERT(this->stackDepth == startDepth - 1); return true; diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp index 74f6389c1..256f247b0 100644 --- a/js/src/vm/AsyncIteration.cpp +++ b/js/src/vm/AsyncIteration.cpp @@ -26,7 +26,7 @@ using namespace js::gc; #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 #define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 -// Async Iteration proposal 2.3.10 Runtime Semantics: EvaluateBody. +// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody. static bool WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) { @@ -128,11 +128,7 @@ js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) return unwrapped; } -static MOZ_MUST_USE bool -AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, - CompletionKind completionKind, HandleValue argument); - -// Async Iteration proposal 5.1.1 Await Fulfilled Functions. +// Async Iteration proposal 4.1.1 Await Fulfilled Functions. MOZ_MUST_USE bool js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle asyncGenObj, HandleValue value) @@ -140,7 +136,7 @@ js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); } -// Async Iteration proposal 5.1.2 Await Rejected Functions. +// Async Iteration proposal 4.1.2 Await Rejected Functions. MOZ_MUST_USE bool js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle asyncGenObj, HandleValue reason) @@ -148,12 +144,30 @@ js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle a return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); } +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle asyncGenObj, + HandleValue value) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value); +} + +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + const Class AsyncFromSyncIteratorObject::class_ = { "AsyncFromSyncIteratorObject", JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) }; -// Async Iteration proposal 6.1.3.1. +// Async Iteration proposal 11.1.3.1. JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) { @@ -164,7 +178,7 @@ js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) return AsyncFromSyncIteratorObject::create(cx, iter); } -// Async Iteration proposal 6.1.3.1 steps 2-4. +// Async Iteration proposal 11.1.3.1 steps 2-4. /* static */ JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) { @@ -187,7 +201,7 @@ AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) return asyncIter; } -// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) { @@ -195,7 +209,7 @@ AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); } -// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) { @@ -203,7 +217,7 @@ AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); } -// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) { @@ -211,7 +225,7 @@ AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); } -// Async Iteration proposal 6.4.1.2 AsyncGenerator.prototype.next. +// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next. static bool AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) { @@ -222,7 +236,7 @@ AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) args.rval()); } -// Async Iteration proposal 6.4.1.3 AsyncGenerator.prototype.return. +// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return. static bool AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) { @@ -233,7 +247,7 @@ AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) args.rval()); } -// Async Iteration proposal 6.4.1.4 AsyncGenerator.prototype.throw. +// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw. static bool AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) { @@ -371,7 +385,7 @@ const Class AsyncGeneratorRequest::class_ = { JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) }; -// Async Iteration proposal 6.4.3.1. +// Async Iteration proposal 11.4.3.1. /* static */ AsyncGeneratorRequest* AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, HandleValue completionValue_, HandleObject promise_) @@ -387,7 +401,7 @@ AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, return request; } -// Async Iteration proposal 6.4.3.2 steps 5.d-g. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g. static MOZ_MUST_USE bool AsyncGeneratorReturned(JSContext* cx, Handle asyncGenObj, HandleValue value) @@ -402,7 +416,7 @@ AsyncGeneratorReturned(JSContext* cx, Handle asyncGenObj, return AsyncGeneratorResolve(cx, asyncGenObj, value, true); } -// Async Iteration proposal 6.4.3.2 steps 5.d, f. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f. static MOZ_MUST_USE bool AsyncGeneratorThrown(JSContext* cx, Handle asyncGenObj) { @@ -422,85 +436,33 @@ AsyncGeneratorThrown(JSContext* cx, Handle asyncGenObj) return AsyncGeneratorReject(cx, asyncGenObj, value); } -// Async Iteration proposal 6.4.3.5. -MOZ_MUST_USE bool -js::AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj) -{ - // Step 1 (implicit). - - // Steps 2-3. - MOZ_ASSERT(!asyncGenObj->isExecuting()); - - // Steps 4-5. - if (asyncGenObj->isQueueEmpty()) - return true; - - // Steps 6-7. - Rooted request( - cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); - if (!request) - return false; - - // Step 8. - CompletionKind completionKind = request->completionKind(); - - // Step 9. - if (completionKind != CompletionKind::Normal) { - // Step 9.a. - if (asyncGenObj->isSuspendedStart()) - asyncGenObj->setCompleted(); - - // Step 9.b. - if (asyncGenObj->isCompleted()) { - // Step 9.b.i. - RootedValue value(cx, request->completionValue()); - if (completionKind == CompletionKind::Return) - return AsyncGeneratorResolve(cx, asyncGenObj, value, true); - // Step 9.b.ii. - return AsyncGeneratorReject(cx, asyncGenObj, value); - } - } else if (asyncGenObj->isCompleted()) { - // Step 10. - return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); - } - - // Step 11. - MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); - - // Step 15 (reordered). - asyncGenObj->setExecuting(); - - RootedValue argument(cx, request->completionValue()); - - // Steps 12-14, 16-20. - return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); -} - -// Async Iteration proposal 6.2.1.3 (partially). +// Async Iteration proposal 11.4.3.7 (partially). // Most steps are done in generator. static MOZ_MUST_USE bool -AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, - HandleValue value) +AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, HandleValue value) { - // Step 5. + // Step 5 is done in bytecode. + + // Step 6. asyncGenObj->setSuspendedYield(); - // Step 8. + // Step 9. return AsyncGeneratorResolve(cx, asyncGenObj, value, false); } -// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20. -// Async Iteration proposal 6.2.1.2 step 10. -// Async Iteration proposal 6.4.3.2 step 5.f-g. -// Async Iteration proposal 5.1 steps 2-9. +// Async Iteration proposal 4.1 Await steps 2-9. +// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g. +// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext +// steps 12-14, 16-20. // Execution context switching is handled in generator. -static MOZ_MUST_USE bool -AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, - CompletionKind completionKind, HandleValue argument) +MOZ_MUST_USE bool +js::AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument) { RootedValue generatorVal(cx, asyncGenObj->generatorVal()); - // 6.4.3.5 steps 12-14, 16-20. + // 11.4.3.5 steps 12-14, 16-20. HandlePropertyName funName = completionKind == CompletionKind::Normal ? cx->names().StarGeneratorNext : completionKind == CompletionKind::Throw @@ -510,10 +472,11 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, args[0].set(argument); RootedValue result(cx); if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { - // 6.4.3.2 step 5.d, f. + // 11.4.3.2 step 5.d, f. return AsyncGeneratorThrown(cx, asyncGenObj); } + // 4.1 steps 2-9. if (asyncGenObj->generatorObj()->isAfterAwait()) return AsyncGeneratorAwait(cx, asyncGenObj, result); @@ -525,8 +488,10 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, // object and it's not observable. // For yield*, it's done on a possibly user-provided result object, and // it's observable. + // + // Note that IteratorComplete steps in 8.2.1 are done in bytecode. - // 2.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. + // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. RootedObject resultObj(cx, &result.toObject()); RootedValue value(cx); @@ -536,7 +501,7 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, if (asyncGenObj->generatorObj()->isAfterYield()) return AsyncGeneratorYield(cx, asyncGenObj, value); - // 6.4.3.2 step 5.d-g. + // 11.4.3.2 step 5.d-g. return AsyncGeneratorReturned(cx, asyncGenObj, value); } @@ -565,14 +530,14 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) return true; - // Async Iteration proposal 6.1.2 %AsyncIteratorPrototype%. + // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%. RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype(cx, global)); if (!asyncIterProto) return false; if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) return false; - // Async Iteration proposal 6.1.3.2 %AsyncFromSyncIteratorPrototype%. + // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%. RootedObject asyncFromSyncIterProto( cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, asyncIterProto)); @@ -585,7 +550,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) return false; } - // Async Iteration proposal 6.4.1 %AsyncGeneratorPrototype%. + // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%. RootedObject asyncGenProto( cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, asyncIterProto)); @@ -597,7 +562,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) return false; } - // Async Iteration proposal 6.3.3 %AsyncGenerator%. + // Async Iteration proposal 11.3.3 %AsyncGenerator%. RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); if (!asyncGenerator) return false; @@ -616,7 +581,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) RootedObject proto(cx, &function.toObject()); RootedAtom name(cx, cx->names().AsyncGeneratorFunction); - // Async Iteration proposal 6.3.2 %AsyncGeneratorFunction%. + // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%. RootedObject asyncGenFunction( cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR, nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject)); diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h index 974c209a0..58c43131b 100644 --- a/js/src/vm/AsyncIteration.h +++ b/js/src/vm/AsyncIteration.h @@ -38,11 +38,18 @@ GetUnwrappedAsyncGenerator(JSFunction* wrapped); MOZ_MUST_USE bool AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle asyncGenObj, - HandleValue value); - + HandleValue value); MOZ_MUST_USE bool AsyncGeneratorAwaitedRejected(JSContext* cx, Handle asyncGenObj, - HandleValue reason); + HandleValue reason); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle asyncGenObj, + HandleValue reason); class AsyncGeneratorRequest : public NativeObject { @@ -97,6 +104,12 @@ class AsyncGeneratorObject : public NativeObject State_SuspendedStart, State_SuspendedYield, State_Executing, + // State_AwaitingYieldReturn corresponds to the case that + // AsyncGenerator#return is called while State_Executing, + // just like the case that AsyncGenerator#return is called + // while State_Completed. + State_AwaitingYieldReturn, + State_AwaitingReturn, State_Completed }; @@ -155,6 +168,12 @@ class AsyncGeneratorObject : public NativeObject bool isExecuting() const { return state() == State_Executing; } + bool isAwaitingYieldReturn() const { + return state() == State_AwaitingYieldReturn; + } + bool isAwaitingReturn() const { + return state() == State_AwaitingReturn; + } bool isCompleted() const { return state() == State_Completed; } @@ -168,6 +187,12 @@ class AsyncGeneratorObject : public NativeObject void setExecuting() { setState(State_Executing); } + void setAwaitingYieldReturn() { + setState(State_AwaitingYieldReturn); + } + void setAwaitingReturn() { + setState(State_AwaitingReturn); + } void setCompleted() { setState(State_Completed); } @@ -223,7 +248,8 @@ class AsyncFromSyncIteratorObject : public NativeObject }; MOZ_MUST_USE bool -AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); +AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument); } // namespace js -- cgit v1.2.3