summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--js/src/builtin/Promise.cpp253
1 files changed, 251 insertions, 2 deletions
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 8fd39e7bb..59e710935 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -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)