mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 14:58:37 +09:00
Bug 1331092 - Part 2: Implement Async Generator except yield*.
Tag #1287
This commit is contained in:
parent
10b93afff4
commit
b5bcbfe58c
1 changed files with 251 additions and 2 deletions
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue