Bug 1331092 - Part 2: Implement Async Generator except yield*.

Tag #1287
This commit is contained in:
Gaming4JC 2019-12-13 21:48:49 -05:00 committed by Roy Tam
commit b5bcbfe58c

View file

@ -12,10 +12,12 @@
#include "jscntxt.h"
#include "jsexn.h"
#include "jsiter.h"
#include "gc/Heap.h"
#include "js/Debug.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "jsobjinlines.h"
@ -36,6 +38,16 @@ enum PromiseHandler {
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,
};
enum ResolutionMode {
@ -178,6 +190,7 @@ enum ReactionRecordSlots {
ReactionRecordSlot_IncumbentGlobalObject,
ReactionRecordSlot_Flags,
ReactionRecordSlot_HandlerArg,
ReactionRecordSlot_Generator,
ReactionRecordSlots,
};
@ -185,6 +198,7 @@ enum ReactionRecordSlots {
#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
// ES2016, 25.4.1.2.
class PromiseReactionRecord : public NativeObject
@ -220,6 +234,22 @@ class PromiseReactionRecord : public NativeObject
int32_t flags = this->flags();
return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT;
}
void setIsAsyncGeneratorAwait(Handle<AsyncGeneratorObject*> asyncGenObj) {
int32_t flags = this->flags();
flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj));
}
bool isAsyncGeneratorAwait() {
int32_t flags = this->flags();
return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT;
}
AsyncGeneratorObject* asyncGenerator() {
MOZ_ASSERT(isAsyncGeneratorAwait());
return &getFixedSlot(ReactionRecordSlot_Generator).toObject()
.as<AsyncGeneratorObject>();
}
Value handler() {
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
uint32_t slot = targetState() == JS::PromiseState::Fulfilled
@ -846,6 +876,34 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord
return true;
}
static MOZ_MUST_USE bool
AsyncGeneratorAwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
MutableHandleValue rval)
{
MOZ_ASSERT(reaction->isAsyncGeneratorAwait());
RootedValue handlerVal(cx, reaction->handler());
RootedValue argument(cx, reaction->handlerArg());
Rooted<AsyncGeneratorObject*> 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 (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument))
return false;
} else {
if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument))
return false;
}
rval.setUndefined();
return true;
}
// ES2016, 25.4.2.1.
/**
* Callback triggering the fulfill/reject reaction for a resolved Promise,
@ -891,6 +949,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
if (reaction->isAsyncFunctionAwait())
return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval());
if (reaction->isAsyncGeneratorAwait())
return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval());
// Step 3.
RootedValue handlerVal(cx, reaction->handler());
@ -907,11 +967,21 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
// Step 4.
if (handlerNum == PromiseHandlerIdentity) {
handlerResult = argument;
} else {
} else if (handlerNum == PromiseHandlerThrower) {
// Step 5.
MOZ_ASSERT(handlerNum == PromiseHandlerThrower);
resolutionMode = RejectMode;
handlerResult = argument;
} else {
MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone ||
handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone);
bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone;
// Async Iteration proposal 6.1.1.2.1 step 1.
RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done));
if (!resultObj)
return false;
handlerResult = ObjectValue(*resultObj);
}
} else {
// Step 6.
@ -2221,6 +2291,185 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand
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.
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
// Async Iteration proposal 6.4.3.3.
MOZ_MUST_USE bool
js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue value, bool done)
{
// Step 1 (implicit).
// Steps 2-3.
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
// Step 4.
Rooted<AsyncGeneratorRequest*> request(
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
if (!request)
return false;
// Step 5.
RootedObject resultPromise(cx, request->promise());
// Step 6.
Rooted<PromiseObject*> 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))
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))
return false;
// Step 12.
return true;
}
// Async Iteration proposal 6.4.3.4.
MOZ_MUST_USE bool
js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
HandleValue exception)
{
// Step 1 (implicit).
// Steps 2-3.
MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
// Step 4.
Rooted<AsyncGeneratorRequest*> request(
cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
if (!request)
return false;
// Step 5.
RootedObject resultPromise(cx, request->promise());
// Step 6.
if (!RejectMaybeWrappedPromise(cx, resultPromise, exception))
return false;
// Step 7.
if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
return false;
// Step 8.
return true;
}
// Async Iteration proposal 6.4.3.6.
MOZ_MUST_USE bool
js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal,
CompletionKind completionKind, HandleValue completionValue,
MutableHandleValue result)
{
// Step 1 (implicit).
// Step 2.
RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!resultPromise)
return false;
// Step 3.
if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is<AsyncGeneratorObject>()) {
// Step 3.a.
RootedValue badGeneratorError(cx);
if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError))
return false;
// Step 3.b.
if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
return false;
// Step 3.c.
result.setObject(*resultPromise);
return true;
}
Rooted<AsyncGeneratorObject*> asyncGenObj(
cx, &asyncGenVal.toObject().as<AsyncGeneratorObject>());
// Step 5 (reordered).
Rooted<AsyncGeneratorRequest*> request(
cx, AsyncGeneratorRequest::create(cx, completionKind, completionValue, resultPromise));
if (!request)
return false;
// Steps 4, 6.
if (!AsyncGeneratorObject::enqueueRequest(cx, asyncGenObj, request))
return false;
// Step 7.
if (!asyncGenObj->isExecuting()) {
// Step 8.
if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
return false;
}
// Step 9.
result.setObject(*resultPromise);
return true;
}
// ES2016, 25.4.5.3.
bool
js::Promise_then(JSContext* cx, unsigned argc, Value* vp)