Bug 1379525 - Part 1: Await on the value before yielding or returning inside async generator.

Tag #1287
This commit is contained in:
Gaming4JC 2019-12-14 10:30:54 -05:00 • committed by Roy Tam
commit 9e8369d01f
4 changed files with 344 additions and 258 deletions

View file

@ -36,18 +36,30 @@ MillisecondsSinceStartup()
enum PromiseHandler { enum PromiseHandler {
PromiseHandlerIdentity = 0, PromiseHandlerIdentity = 0,
PromiseHandlerThrower, PromiseHandlerThrower,
PromiseHandlerAsyncFunctionAwaitFulfilled,
PromiseHandlerAsyncFunctionAwaitRejected,
PromiseHandlerAsyncGeneratorAwaitFulfilled,
PromiseHandlerAsyncGeneratorAwaitRejected,
// Async Iteration proposal 6.1.1.2.1. // ES 2018 draft 25.5.5.4-5.
// Async iterator handlers take the resolved value and create new iterator PromiseHandlerAsyncFunctionAwaitedFulfilled,
// objects. To do so it needs to forward whether the iterator is done. In PromiseHandlerAsyncFunctionAwaitedRejected,
// spec, this is achieved via the [[Done]] internal slot. We enumerate both
// true and false cases here. // Async Iteration proposal 4.1.
PromiseHandlerAsyncIteratorValueUnwrapDone, PromiseHandlerAsyncGeneratorAwaitedFulfilled,
PromiseHandlerAsyncIteratorValueUnwrapNotDone, 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 { enum ResolutionMode {
@ -197,8 +209,8 @@ enum ReactionRecordSlots {
#define REACTION_FLAG_RESOLVED 0x1 #define REACTION_FLAG_RESOLVED 0x1
#define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_FULFILLED 0x2
#define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4
#define REACTION_FLAG_ASYNC_FUNCTION_AWAIT 0x8 #define REACTION_FLAG_ASYNC_FUNCTION 0x8
#define REACTION_FLAG_ASYNC_GENERATOR_AWAIT 0x10 #define REACTION_FLAG_ASYNC_GENERATOR 0x10
// ES2016, 25.4.1.2. // ES2016, 25.4.1.2.
class PromiseReactionRecord : public NativeObject class PromiseReactionRecord : public NativeObject
@ -225,28 +237,28 @@ class PromiseReactionRecord : public NativeObject
flags |= REACTION_FLAG_FULFILLED; flags |= REACTION_FLAG_FULFILLED;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
} }
void setIsAsyncFunctionAwait() { void setIsAsyncFunction() {
int32_t flags = this->flags(); int32_t flags = this->flags();
flags |= REACTION_FLAG_ASYNC_FUNCTION_AWAIT; flags |= REACTION_FLAG_ASYNC_FUNCTION;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
} }
bool isAsyncFunctionAwait() { bool isAsyncFunction() {
int32_t flags = this->flags(); int32_t flags = this->flags();
return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT; return flags & REACTION_FLAG_ASYNC_FUNCTION;
} }
void setIsAsyncGeneratorAwait(Handle<AsyncGeneratorObject*> asyncGenObj) { void setIsAsyncGenerator(Handle<AsyncGeneratorObject*> asyncGenObj) {
int32_t flags = this->flags(); int32_t flags = this->flags();
flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT; flags |= REACTION_FLAG_ASYNC_GENERATOR;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj)); setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj));
} }
bool isAsyncGeneratorAwait() { bool isAsyncGenerator() {
int32_t flags = this->flags(); int32_t flags = this->flags();
return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT; return flags & REACTION_FLAG_ASYNC_GENERATOR;
} }
AsyncGeneratorObject* asyncGenerator() { AsyncGeneratorObject* asyncGenerator() {
MOZ_ASSERT(isAsyncGeneratorAwait()); MOZ_ASSERT(isAsyncGenerator());
return &getFixedSlot(ReactionRecordSlot_Generator).toObject() return &getFixedSlot(ReactionRecordSlot_Generator).toObject()
.as<AsyncGeneratorObject>(); .as<AsyncGeneratorObject>();
} }
@ -848,10 +860,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat
} }
static MOZ_MUST_USE bool static MOZ_MUST_USE bool
AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, AsyncFunctionPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
MutableHandleValue rval) MutableHandleValue rval)
{ {
MOZ_ASSERT(reaction->isAsyncFunctionAwait()); MOZ_ASSERT(reaction->isAsyncFunction());
RootedValue handlerVal(cx, reaction->handler()); RootedValue handlerVal(cx, reaction->handler());
RootedValue argument(cx, reaction->handlerArg()); RootedValue argument(cx, reaction->handlerArg());
@ -859,15 +871,14 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord
RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator)); RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator));
int32_t handlerNum = int32_t(handlerVal.toNumber()); int32_t handlerNum = int32_t(handlerVal.toNumber());
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled ||
handlerNum == PromiseHandlerAsyncFunctionAwaitRejected);
// Await's handlers don't return a value, nor throw exception. // Await's handlers don't return a value, nor throw exception.
// They fail only on OOM. // They fail only on OOM.
if (handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled) { if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument))
return false; return false;
} else { } else {
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected);
if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument)) if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument))
return false; return false;
} }
@ -877,27 +888,48 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord
} }
static MOZ_MUST_USE bool static MOZ_MUST_USE bool
AsyncGeneratorAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction, AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
MutableHandleValue rval) MutableHandleValue rval)
{ {
MOZ_ASSERT(reaction->isAsyncGeneratorAwait()); MOZ_ASSERT(reaction->isAsyncGenerator());
RootedValue handlerVal(cx, reaction->handler()); RootedValue handlerVal(cx, reaction->handler());
RootedValue argument(cx, reaction->handlerArg()); RootedValue argument(cx, reaction->handlerArg());
Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator()); Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator());
int32_t handlerNum = int32_t(handlerVal.toNumber()); int32_t handlerNum = int32_t(handlerVal.toNumber());
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled ||
handlerNum == PromiseHandlerAsyncGeneratorAwaitRejected);
// Await's handlers don't return a value, nor throw exception. // Await's handlers don't return a value, nor throw exception.
// They fail only on OOM. // They fail only on OOM.
if (handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled) { if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) {
// 4.1.1.
if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument)) if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument))
return false; return false;
} else { } else if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedRejected) {
// 4.1.2.
if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument)) if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument))
return false; 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(); rval.setUndefined();
@ -947,10 +979,10 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
// Steps 1-2. // Steps 1-2.
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>()); Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
if (reaction->isAsyncFunctionAwait()) if (reaction->isAsyncFunction())
return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval()); return AsyncFunctionPromiseReactionJob(cx, reaction, args.rval());
if (reaction->isAsyncGeneratorAwait()) if (reaction->isAsyncGenerator())
return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval()); return AsyncGeneratorPromiseReactionJob(cx, reaction, args.rval());
// Step 3. // Step 3.
RootedValue handlerVal(cx, reaction->handler()); RootedValue handlerVal(cx, reaction->handler());
@ -972,11 +1004,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
resolutionMode = RejectMode; resolutionMode = RejectMode;
handlerResult = argument; handlerResult = argument;
} else { } else {
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone || MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone ||
handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone); handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone);
bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone; bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone;
// Async Iteration proposal 6.1.1.2.1 step 1. // Async Iteration proposal 11.1.3.2.5 step 1.
RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done));
if (!resultObj) if (!resultObj)
return false; return false;
@ -2214,7 +2246,7 @@ static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx,
// Some async/await functions are implemented here instead of // Some async/await functions are implemented here instead of
// js/src/builtin/AsyncFunction.cpp, to call Promise internal functions. // 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* MOZ_MUST_USE PromiseObject*
js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
{ {
@ -2228,7 +2260,7 @@ js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal)
return promise; 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 MOZ_MUST_USE bool
js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise) js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
{ {
@ -2244,7 +2276,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise)
return true; 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 MOZ_MUST_USE bool
js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
{ {
@ -2256,28 +2288,27 @@ js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, H
return true; return true;
} }
// Async Functions proposal 2.3 steps 2-8. // Helper function that performs the equivalent steps as
MOZ_MUST_USE bool // Async Iteration proposal 4.1 Await steps 2-3, 6-9 or similar.
js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) template <typename T>
static MOZ_MUST_USE bool
InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise,
HandleValue onFulfilled, HandleValue onRejected, T extraStep)
{ {
// Step 2. // Step 2.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise) if (!promise)
return false; return false;
// Steps 3. // Step 3.
if (!ResolvePromiseInternal(cx, promise, value)) if (!ResolvePromiseInternal(cx, promise, value))
return false; return false;
// Steps 4-5.
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitFulfilled));
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitRejected));
RootedObject incumbentGlobal(cx); RootedObject incumbentGlobal(cx);
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
return false; return false;
// Steps 6-7. // Steps 7-8.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise,
onFulfilled, onRejected, onFulfilled, onRejected,
nullptr, nullptr, nullptr, nullptr,
@ -2285,53 +2316,47 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand
if (!reaction) if (!reaction)
return false; return false;
reaction->setIsAsyncFunctionAwait(); // Step 6.
extraStep(reaction);
// Step 8.
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
// Async Iteration proposal 5.1 steps 2-9.
MOZ_MUST_USE bool
js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value)
{
// Step 2.
Rooted<PromiseObject*> 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;
// Step 6 (skipped).
// Steps 7-8.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, nullptr,
onFulfilled, onRejected,
nullptr, nullptr,
incumbentGlobal));
if (!reaction)
return false;
reaction->setIsAsyncGeneratorAwait(asyncGenObj);
// Step 9. // Step 9.
return PerformPromiseThenWithReaction(cx, promise, reaction); return PerformPromiseThenWithReaction(cx, promise, reaction);
} }
// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next // ES 2018 draft 25.5.5.3 steps 2-10.
// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return MOZ_MUST_USE bool
// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
{
// Steps 4-5.
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled));
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected));
// Steps 2-3, 6-10.
auto extra = [](Handle<PromiseReactionRecord*> reaction) {
reaction->setIsAsyncFunction();
};
return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra);
}
// Async Iteration proposal 4.1 Await steps 2-9.
MOZ_MUST_USE bool
js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value)
{
// Steps 4-5.
RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled));
RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected));
// Steps 2-3, 6-9.
auto extra = [&](Handle<PromiseReactionRecord*> reaction) {
reaction->setIsAsyncGenerator(asyncGenObj);
};
return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra);
}
// 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 bool
js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind)
{ {
@ -2368,11 +2393,11 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
RootedValue resultVal(cx); RootedValue resultVal(cx);
RootedValue func(cx); RootedValue func(cx);
if (completionKind == CompletionKind::Normal) { 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)) if (!GetProperty(cx, iter, iter, cx->names().next, &func))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
} else if (completionKind == CompletionKind::Return) { } 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)) if (!GetProperty(cx, iter, iter, cx->names().return_, &func))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
@ -2394,7 +2419,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
return true; return true;
} }
} else { } else {
// 6.1.3.2.3 steps 5-6. // 11.1.3.2.3 steps 5-6.
MOZ_ASSERT(completionKind == CompletionKind::Throw); MOZ_ASSERT(completionKind == CompletionKind::Throw);
if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) if (!GetProperty(cx, iter, iter, cx->names().throw_, &func))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); 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). // 11.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.2, 11.1.3.2.3 steps 8-9.
RootedValue iterVal(cx, ObjectValue(*iter)); RootedValue iterVal(cx, ObjectValue(*iter));
FixedInvokeArgs<1> args2(cx); FixedInvokeArgs<1> args2(cx);
args2[0].set(args.get(0)); args2[0].set(args.get(0));
if (!js::Call(cx, func, iterVal, args2, &resultVal)) if (!js::Call(cx, func, iterVal, args2, &resultVal))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
// 6.1.3.2.1 steps 5-6 (partially). // 11.1.3.2.1 steps 5-6 (partially).
// 6.1.3.2.2, 6.1.3.2.3 steps 10. // 11.1.3.2.2, 11.1.3.2.3 steps 10.
if (!resultVal.isObject()) { if (!resultVal.isObject()) {
CheckIsObjectKind kind; CheckIsObjectKind kind;
switch (completionKind) { switch (completionKind) {
@ -2440,8 +2465,8 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
RootedObject resultObj(cx, &resultVal.toObject()); RootedObject resultObj(cx, &resultVal.toObject());
// Following step numbers are for 6.1.3.2.1. // Following step numbers are for 11.1.3.2.1.
// For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20. // For 11.1.3.2.2 and 11.1.3.2.3, steps 7-16 corresponds to steps 11-20.
// Steps 7-8. // Steps 7-8.
RootedValue doneVal(cx); RootedValue doneVal(cx);
@ -2454,33 +2479,15 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
return AbruptRejectPromise(cx, args, resultPromise, nullptr); return AbruptRejectPromise(cx, args, resultPromise, nullptr);
// Step 11.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise)
return false;
// Step 12.
if (!ResolvePromiseInternal(cx, promise, value))
return false;
// Steps 13-14. // Steps 13-14.
RootedValue onFulfilled(cx, Int32Value(done RootedValue onFulfilled(cx, Int32Value(done
? PromiseHandlerAsyncIteratorValueUnwrapDone ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone
: PromiseHandlerAsyncIteratorValueUnwrapNotDone)); : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone));
RootedObject incumbentGlobal(cx); // Steps 11-12, 15.
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) auto extra = [](Handle<PromiseReactionRecord*> reaction) {
return false; };
if (!InternalAwait(cx, value, resultPromise, onFulfilled, UndefinedHandleValue, extra))
// Step 15.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled,
UndefinedHandleValue,
nullptr, nullptr,
incumbentGlobal));
if (!reaction)
return false;
if (!PerformPromiseThenWithReaction(cx, promise, reaction))
return false; return false;
// Step 16. // Step 16.
@ -2488,7 +2495,10 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co
return true; return true;
} }
// Async Iteration proposal 6.4.3.3. static MOZ_MUST_USE bool
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
// Async Iteration proposal 11.4.3.3.
MOZ_MUST_USE bool MOZ_MUST_USE bool
js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value, bool done) HandleValue value, bool done)
@ -2508,43 +2518,25 @@ js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenO
RootedObject resultPromise(cx, request->promise()); RootedObject resultPromise(cx, request->promise());
// Step 6. // Step 6.
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
if (!promise) if (!resultObj)
return false; return false;
RootedValue resultValue(cx, ObjectValue(*resultObj));
// Step 7. // Step 7.
if (!ResolvePromiseInternal(cx, promise, value)) if (!ResolvePromiseInternal(cx, resultPromise, resultValue))
return false; return false;
// Steps 8-9. // Step 8.
RootedValue onFulfilled(cx, Int32Value(done
? PromiseHandlerAsyncIteratorValueUnwrapDone
: PromiseHandlerAsyncIteratorValueUnwrapNotDone));
RootedObject incumbentGlobal(cx);
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
return false;
// Step 10.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled,
UndefinedHandleValue,
nullptr, nullptr,
incumbentGlobal));
if (!reaction)
return false;
if (!PerformPromiseThenWithReaction(cx, promise, reaction))
return false;
// Step 11.
if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
return false; return false;
// Step 12. // Step 9.
return true; return true;
} }
// Async Iteration proposal 6.4.3.4. // Async Iteration proposal 11.4.3.4.
MOZ_MUST_USE bool MOZ_MUST_USE bool
js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue exception) HandleValue exception)
@ -2575,7 +2567,98 @@ js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenOb
return true; 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<AsyncGeneratorObject*> 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<AsyncGeneratorRequest*> 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<PromiseReactionRecord*> 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<PromiseReactionRecord*> 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 MOZ_MUST_USE bool
js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
CompletionKind completionKind, HandleValue completionValue, CompletionKind completionKind, HandleValue completionValue,

View file

@ -8578,6 +8578,13 @@ BytecodeEmitter::emitReturn(ParseNode* pn)
if (ParseNode* pn2 = pn->pn_kid) { if (ParseNode* pn2 = pn->pn_kid) {
if (!emitTree(pn2)) if (!emitTree(pn2))
return false; return false;
bool isAsyncGenerator = sc->asFunctionBox()->isAsync() &&
sc->asFunctionBox()->isStarGenerator();
if (isAsyncGenerator) {
if (!emitAwait())
return false;
}
} else { } else {
/* No explicit return value provided */ /* No explicit return value provided */
if (!emit1(JSOP_UNDEFINED)) if (!emit1(JSOP_UNDEFINED))
@ -8690,6 +8697,14 @@ BytecodeEmitter::emitYield(ParseNode* pn)
if (!emit1(JSOP_UNDEFINED)) if (!emit1(JSOP_UNDEFINED))
return false; return false;
} }
// 11.4.3.7 AsyncGeneratorYield step 5.
bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
if (isAsyncGenerator) {
if (!emitAwait()) // RESULT
return false;
}
if (needsIteratorResult) { if (needsIteratorResult) {
if (!emitFinishIteratorResult(false)) if (!emitFinishIteratorResult(false))
return false; return false;
@ -8762,6 +8777,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
MOZ_ASSERT(this->stackDepth == startDepth); MOZ_ASSERT(this->stackDepth == startDepth);
// 11.4.3.7 AsyncGeneratorYield step 5.
if (isAsyncGenerator) {
if (!emitAwait()) // ITER RESULT
return false;
}
// Load the generator object. // Load the generator object.
if (!emitGetDotGenerator()) // ITER RESULT GENOBJ if (!emitGetDotGenerator()) // ITER RESULT GENOBJ
return false; return false;
@ -8910,10 +8931,6 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
return false; return false;
if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE
return false; return false;
if (isAsyncGenerator) {
if (!emitAwait()) // ITER OLDRESULT FTYPE FVALUE VALUE
return false;
}
if (!emitPrepareIteratorResult()) // ITER OLDRESULT FTYPE FVALUE VALUE RESULT if (!emitPrepareIteratorResult()) // ITER OLDRESULT FTYPE FVALUE VALUE RESULT
return false; return false;
if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RESULT VALUE 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 if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // VALUE
return false; return false;
if (isAsyncGenerator) {
if (!emitAwait()) // VALUE
return false;
}
MOZ_ASSERT(this->stackDepth == startDepth - 1); MOZ_ASSERT(this->stackDepth == startDepth - 1);
return true; return true;

View file

@ -26,7 +26,7 @@ using namespace js::gc;
#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 #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 static bool
WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -128,11 +128,7 @@ js::GetUnwrappedAsyncGenerator(JSFunction* wrapped)
return unwrapped; return unwrapped;
} }
static MOZ_MUST_USE bool // Async Iteration proposal 4.1.1 Await Fulfilled Functions.
AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue argument);
// Async Iteration proposal 5.1.1 Await Fulfilled Functions.
MOZ_MUST_USE bool MOZ_MUST_USE bool
js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value) HandleValue value)
@ -140,7 +136,7 @@ js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*>
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); 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 MOZ_MUST_USE bool
js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason) HandleValue reason)
@ -148,12 +144,30 @@ js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> a
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); 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<AsyncGeneratorObject*> 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<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason)
{
return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
}
const Class AsyncFromSyncIteratorObject::class_ = { const Class AsyncFromSyncIteratorObject::class_ = {
"AsyncFromSyncIteratorObject", "AsyncFromSyncIteratorObject",
JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)
}; };
// Async Iteration proposal 6.1.3.1. // Async Iteration proposal 11.1.3.1.
JSObject* JSObject*
js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
{ {
@ -164,7 +178,7 @@ js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
return AsyncFromSyncIteratorObject::create(cx, 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* /* static */ JSObject*
AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
{ {
@ -187,7 +201,7 @@ AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
return asyncIter; return asyncIter;
} }
// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. // Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
static bool static bool
AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -195,7 +209,7 @@ AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); 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 static bool
AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -203,7 +217,7 @@ AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); 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 static bool
AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -211,7 +225,7 @@ AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); 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 static bool
AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -222,7 +236,7 @@ AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
args.rval()); args.rval());
} }
// Async Iteration proposal 6.4.1.3 AsyncGenerator.prototype.return. // Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return.
static bool static bool
AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -233,7 +247,7 @@ AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
args.rval()); args.rval());
} }
// Async Iteration proposal 6.4.1.4 AsyncGenerator.prototype.throw. // Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw.
static bool static bool
AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp)
{ {
@ -371,7 +385,7 @@ const Class AsyncGeneratorRequest::class_ = {
JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)
}; };
// Async Iteration proposal 6.4.3.1. // Async Iteration proposal 11.4.3.1.
/* static */ AsyncGeneratorRequest* /* static */ AsyncGeneratorRequest*
AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
HandleValue completionValue_, HandleObject promise_) HandleValue completionValue_, HandleObject promise_)
@ -387,7 +401,7 @@ AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
return request; 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 static MOZ_MUST_USE bool
AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value) HandleValue value)
@ -402,7 +416,7 @@ AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
return AsyncGeneratorResolve(cx, asyncGenObj, value, true); 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 static MOZ_MUST_USE bool
AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
{ {
@ -422,85 +436,33 @@ AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
return AsyncGeneratorReject(cx, asyncGenObj, value); return AsyncGeneratorReject(cx, asyncGenObj, value);
} }
// Async Iteration proposal 6.4.3.5. // Async Iteration proposal 11.4.3.7 (partially).
MOZ_MUST_USE bool
js::AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
{
// Step 1 (implicit).
// Steps 2-3.
MOZ_ASSERT(!asyncGenObj->isExecuting());
// Steps 4-5.
if (asyncGenObj->isQueueEmpty())
return true;
// Steps 6-7.
Rooted<AsyncGeneratorRequest*> 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).
// Most steps are done in generator. // Most steps are done in generator.
static MOZ_MUST_USE bool static MOZ_MUST_USE bool
AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value)
HandleValue value)
{ {
// Step 5. // Step 5 is done in bytecode.
// Step 6.
asyncGenObj->setSuspendedYield(); asyncGenObj->setSuspendedYield();
// Step 8. // Step 9.
return AsyncGeneratorResolve(cx, asyncGenObj, value, false); return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
} }
// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20. // Async Iteration proposal 4.1 Await steps 2-9.
// Async Iteration proposal 6.2.1.2 step 10. // Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
// Async Iteration proposal 6.4.3.2 step 5.f-g. // Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g.
// Async Iteration proposal 5.1 steps 2-9. // Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext
// steps 12-14, 16-20.
// Execution context switching is handled in generator. // Execution context switching is handled in generator.
static MOZ_MUST_USE bool MOZ_MUST_USE bool
AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue argument) CompletionKind completionKind, HandleValue argument)
{ {
RootedValue generatorVal(cx, asyncGenObj->generatorVal()); 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 HandlePropertyName funName = completionKind == CompletionKind::Normal
? cx->names().StarGeneratorNext ? cx->names().StarGeneratorNext
: completionKind == CompletionKind::Throw : completionKind == CompletionKind::Throw
@ -510,10 +472,11 @@ AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
args[0].set(argument); args[0].set(argument);
RootedValue result(cx); RootedValue result(cx);
if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { 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); return AsyncGeneratorThrown(cx, asyncGenObj);
} }
// 4.1 steps 2-9.
if (asyncGenObj->generatorObj()->isAfterAwait()) if (asyncGenObj->generatorObj()->isAfterAwait())
return AsyncGeneratorAwait(cx, asyncGenObj, result); return AsyncGeneratorAwait(cx, asyncGenObj, result);
@ -525,8 +488,10 @@ AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
// object and it's not observable. // object and it's not observable.
// For yield*, it's done on a possibly user-provided result object, and // For yield*, it's done on a possibly user-provided result object, and
// it's observable. // 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()); RootedObject resultObj(cx, &result.toObject());
RootedValue value(cx); RootedValue value(cx);
@ -536,7 +501,7 @@ AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
if (asyncGenObj->generatorObj()->isAfterYield()) if (asyncGenObj->generatorObj()->isAfterYield())
return AsyncGeneratorYield(cx, asyncGenObj, value); 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); return AsyncGeneratorReturned(cx, asyncGenObj, value);
} }
@ -565,14 +530,14 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject())
return true; return true;
// Async Iteration proposal 6.1.2 %AsyncIteratorPrototype%. // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%.
RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!asyncIterProto) if (!asyncIterProto)
return false; return false;
if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
return false; return false;
// Async Iteration proposal 6.1.3.2 %AsyncFromSyncIteratorPrototype%. // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%.
RootedObject asyncFromSyncIterProto( RootedObject asyncFromSyncIterProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
asyncIterProto)); asyncIterProto));
@ -585,7 +550,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
return false; return false;
} }
// Async Iteration proposal 6.4.1 %AsyncGeneratorPrototype%. // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%.
RootedObject asyncGenProto( RootedObject asyncGenProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
asyncIterProto)); asyncIterProto));
@ -597,7 +562,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
return false; return false;
} }
// Async Iteration proposal 6.3.3 %AsyncGenerator%. // Async Iteration proposal 11.3.3 %AsyncGenerator%.
RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!asyncGenerator) if (!asyncGenerator)
return false; return false;
@ -616,7 +581,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
RootedObject proto(cx, &function.toObject()); RootedObject proto(cx, &function.toObject());
RootedAtom name(cx, cx->names().AsyncGeneratorFunction); RootedAtom name(cx, cx->names().AsyncGeneratorFunction);
// Async Iteration proposal 6.3.2 %AsyncGeneratorFunction%. // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%.
RootedObject asyncGenFunction( RootedObject asyncGenFunction(
cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR, cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR,
nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject)); nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject));

View file

@ -38,11 +38,18 @@ GetUnwrappedAsyncGenerator(JSFunction* wrapped);
MOZ_MUST_USE bool MOZ_MUST_USE bool
AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value); HandleValue value);
MOZ_MUST_USE bool MOZ_MUST_USE bool
AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason); HandleValue reason);
MOZ_MUST_USE bool
AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value);
MOZ_MUST_USE bool
AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue reason);
class AsyncGeneratorRequest : public NativeObject class AsyncGeneratorRequest : public NativeObject
{ {
@ -97,6 +104,12 @@ class AsyncGeneratorObject : public NativeObject
State_SuspendedStart, State_SuspendedStart,
State_SuspendedYield, State_SuspendedYield,
State_Executing, 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 State_Completed
}; };
@ -155,6 +168,12 @@ class AsyncGeneratorObject : public NativeObject
bool isExecuting() const { bool isExecuting() const {
return state() == State_Executing; return state() == State_Executing;
} }
bool isAwaitingYieldReturn() const {
return state() == State_AwaitingYieldReturn;
}
bool isAwaitingReturn() const {
return state() == State_AwaitingReturn;
}
bool isCompleted() const { bool isCompleted() const {
return state() == State_Completed; return state() == State_Completed;
} }
@ -168,6 +187,12 @@ class AsyncGeneratorObject : public NativeObject
void setExecuting() { void setExecuting() {
setState(State_Executing); setState(State_Executing);
} }
void setAwaitingYieldReturn() {
setState(State_AwaitingYieldReturn);
}
void setAwaitingReturn() {
setState(State_AwaitingReturn);
}
void setCompleted() { void setCompleted() {
setState(State_Completed); setState(State_Completed);
} }
@ -223,7 +248,8 @@ class AsyncFromSyncIteratorObject : public NativeObject
}; };
MOZ_MUST_USE bool MOZ_MUST_USE bool
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
CompletionKind completionKind, HandleValue argument);
} // namespace js } // namespace js