summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-12-14 09:16:32 -0500
committerGaming4JC <g4jc@hyperbola.info>2019-12-17 06:25:28 -0500
commitef44324d916b89b95fa0ea77a3d91eafb4359bf8 (patch)
tree38ce7009388ceef8cb6793f5d0dcd5388d1d3a3f
parentbbd1fef7840d97801307f9ace021b52d94c5f61f (diff)
downloaduxp-ef44324d916b89b95fa0ea77a3d91eafb4359bf8.tar.gz
Bug 1331092 - Part 7: Implement Async Generator yield*.
Tag #1287
-rw-r--r--js/public/Class.h2
-rw-r--r--js/src/builtin/Promise.cpp159
-rw-r--r--js/src/builtin/Promise.h3
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp130
-rw-r--r--js/src/frontend/BytecodeEmitter.h8
-rw-r--r--js/src/js.msg2
-rw-r--r--js/src/jsiter.h2
-rw-r--r--js/src/vm/AsyncIteration.cpp125
-rw-r--r--js/src/vm/AsyncIteration.h26
-rw-r--r--js/src/vm/CommonPropertyNames.h1
-rw-r--r--js/src/vm/GlobalObject.h7
-rw-r--r--js/src/vm/Interpreter.cpp16
-rw-r--r--js/src/vm/Interpreter.h3
-rw-r--r--js/src/vm/Opcodes.h10
14 files changed, 451 insertions, 43 deletions
diff --git a/js/public/Class.h b/js/public/Class.h
index a7250eac05..f4fa9ccafb 100644
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -779,7 +779,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
- (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 44)
+ (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp
index 59e710935e..d2b7b543b1 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -2329,6 +2329,165 @@ js::AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
+// 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
+bool
+js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind)
+{
+ // Step 1.
+ RootedValue thisVal(cx, args.thisv());
+
+ // Step 2.
+ RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+ if (!resultPromise)
+ return false;
+
+ // Step 3.
+ if (!thisVal.isObject() || !thisVal.toObject().is<AsyncFromSyncIteratorObject>()) {
+ // Step 3.a.
+ RootedValue badGeneratorError(cx);
+ if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError))
+ return false;
+
+ // Step 3.b.
+ if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError))
+ return false;
+
+ // Step 3.c.
+ args.rval().setObject(*resultPromise);
+ return true;
+ }
+
+ Rooted<AsyncFromSyncIteratorObject*> asyncIter(
+ cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>());
+
+ // Step 4.
+ RootedObject iter(cx, asyncIter->iterator());
+
+ RootedValue resultVal(cx);
+ RootedValue func(cx);
+ if (completionKind == CompletionKind::Normal) {
+ // 6.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.
+ if (!GetProperty(cx, iter, iter, cx->names().return_, &func))
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+ // Step 7.
+ if (func.isNullOrUndefined()) {
+ // Step 7.a.
+ RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true));
+ if (!resultObj)
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+ RootedValue resultVal(cx, ObjectValue(*resultObj));
+
+ // Step 7.b.
+ if (!ResolvePromiseInternal(cx, resultPromise, resultVal))
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+ // Step 7.c.
+ args.rval().setObject(*resultPromise);
+ return true;
+ }
+ } else {
+ // 6.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);
+
+ // Step 7.
+ if (func.isNullOrUndefined()) {
+ // Step 7.a.
+ if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0)))
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+ // Step 7.b.
+ args.rval().setObject(*resultPromise);
+ return true;
+ }
+ }
+
+ // 6.1.3.2.1 steps 5-6 (partially).
+ // 6.1.3.2.2, 6.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.
+ if (!resultVal.isObject()) {
+ CheckIsObjectKind kind;
+ switch (completionKind) {
+ case CompletionKind::Normal:
+ kind = CheckIsObjectKind::IteratorNext;
+ break;
+ case CompletionKind::Throw:
+ kind = CheckIsObjectKind::IteratorThrow;
+ break;
+ case CompletionKind::Return:
+ kind = CheckIsObjectKind::IteratorReturn;
+ break;
+ }
+ MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind));
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+ }
+
+ 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.
+
+ // Steps 7-8.
+ RootedValue value(cx);
+ if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+
+ // Steps 9-10.
+ RootedValue doneVal(cx);
+ if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
+ return AbruptRejectPromise(cx, args, resultPromise, nullptr);
+ bool done = ToBoolean(doneVal);
+
+ // Step 11.
+ Rooted<PromiseObject*> 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));
+
+ RootedObject incumbentGlobal(cx);
+ if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
+ return false;
+
+ // 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;
+
+ // Step 16.
+ args.rval().setObject(*resultPromise);
+ return true;
+}
+
// Async Iteration proposal 6.4.3.3.
MOZ_MUST_USE bool
js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h
index 680ab2abd9..b04bd0e2a6 100644
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -168,6 +168,9 @@ MOZ_MUST_USE bool
AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind,
HandleValue completionValue, MutableHandleValue result);
+bool
+AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind);
+
/**
* A PromiseTask represents a task that can be dispatched to a helper thread
* (via StartPromiseTask), executed (by implementing PromiseTask::execute()),
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 9d6a686095..d926e130c1 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2118,7 +2118,7 @@ class ForOfLoopControl : public LoopControl
bool emitIteratorClose(BytecodeEmitter* bce,
CompletionKind completionKind = CompletionKind::Normal) {
ptrdiff_t start = bce->offset();
- if (!bce->emitIteratorClose(completionKind, allowSelfHosted_))
+ if (!bce->emitIteratorClose(IteratorKind::Sync, completionKind, allowSelfHosted_))
return false;
ptrdiff_t end = bce->offset();
return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
@@ -3722,6 +3722,18 @@ BytecodeEmitter::emitFinishIteratorResult(bool done)
}
bool
+BytecodeEmitter::emitToIteratorResult(bool done)
+{
+ if (!emitPrepareIteratorResult()) // VALUE OBJ
+ return false;
+ if (!emit1(JSOP_SWAP)) // OBJ VALUE
+ return false;
+ if (!emitFinishIteratorResult(done)) // RESULT
+ return false;
+ return true;
+}
+
+bool
BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext)
{
switch (loc.kind()) {
@@ -5255,7 +5267,8 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false
}
bool
-BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = CompletionKind::Normal */,
+BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync */,
+ CompletionKind completionKind /* = CompletionKind::Normal */,
bool allowSelfHosted /* = false */)
{
MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
@@ -5340,6 +5353,11 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion
return false;
checkTypeSet(JSOP_CALL);
+ if (iterKind == IteratorKind::Async) {
+ if (!emitAwait()) // ... ... RESULT
+ return false;
+ }
+
if (completionKind == CompletionKind::Throw) {
if (!emit1(JSOP_SWAP)) // ... RET ITER RESULT UNDEF
return false;
@@ -5349,7 +5367,7 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion
if (!tryCatch->emitCatch()) // ... RET ITER RESULT
return false;
- // Just ignore the exception thrown by call.
+ // Just ignore the exception thrown by call and await.
if (!emit1(JSOP_EXCEPTION)) // ... RET ITER RESULT EXC
return false;
if (!emit1(JSOP_POP)) // ... RET ITER RESULT
@@ -6972,6 +6990,63 @@ BytecodeEmitter::emitIterator()
}
bool
+BytecodeEmitter::emitAsyncIterator()
+{
+ // Convert iterable to iterator.
+ if (!emit1(JSOP_DUP)) // OBJ OBJ
+ return false;
+ if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::asyncIterator))) // OBJ OBJ @@ASYNCITERATOR
+ return false;
+ if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN
+ return false;
+
+ IfThenElseEmitter ifAsyncIterIsUndefined(this);
+ if (!emit1(JSOP_DUP)) // OBJ ITERFN ITERFN
+ return false;
+ if (!emit1(JSOP_UNDEFINED)) // OBJ ITERFN ITERFN UNDEF
+ return false;
+ if (!emit1(JSOP_EQ)) // OBJ ITERFN EQ
+ return false;
+ if (!ifAsyncIterIsUndefined.emitIfElse()) // OBJ ITERFN
+ return false;
+
+ if (!emit1(JSOP_POP)) // OBJ
+ return false;
+ if (!emit1(JSOP_DUP)) // OBJ OBJ
+ return false;
+ if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator))) // OBJ OBJ @@ITERATOR
+ return false;
+ if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN
+ return false;
+ if (!emit1(JSOP_SWAP)) // ITERFN OBJ
+ return false;
+ if (!emitCall(JSOP_CALLITER, 0)) // ITER
+ return false;
+ checkTypeSet(JSOP_CALLITER);
+ if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER
+ return false;
+
+ if (!emit1(JSOP_TOASYNCITER)) // ITER
+ return false;
+
+ if (!ifAsyncIterIsUndefined.emitElse()) // OBJ ITERFN
+ return false;
+
+ if (!emit1(JSOP_SWAP)) // ITERFN OBJ
+ return false;
+ if (!emitCall(JSOP_CALLITER, 0)) // ITER
+ return false;
+ checkTypeSet(JSOP_CALLITER);
+ if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER
+ return false;
+
+ if (!ifAsyncIterIsUndefined.emitEnd()) // ITER
+ return false;
+
+ return true;
+}
+
+bool
BytecodeEmitter::emitSpread(bool allowSelfHosted)
{
LoopControl loopInfo(this, StatementKind::Spread);
@@ -8593,6 +8668,16 @@ BytecodeEmitter::emitYield(ParseNode* pn)
}
bool
+BytecodeEmitter::emitAwait()
+{
+ if (!emitGetDotGenerator())
+ return false;
+ if (!emitYieldOp(JSOP_AWAIT))
+ return false;
+ return true;
+}
+
+bool
BytecodeEmitter::emitAwait(ParseNode* pn)
{
MOZ_ASSERT(sc->isFunctionBox());
@@ -8600,11 +8685,7 @@ BytecodeEmitter::emitAwait(ParseNode* pn)
if (!emitTree(pn->pn_kid))
return false;
- if (!emitGetDotGenerator())
- return false;
- if (!emitYieldOp(JSOP_AWAIT))
- return false;
- return true;
+ return emitAwait();
}
bool
@@ -8613,10 +8694,17 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
MOZ_ASSERT(sc->isFunctionBox());
MOZ_ASSERT(sc->asFunctionBox()->isStarGenerator());
+ bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
+
if (!emitTree(iter)) // ITERABLE
return false;
- if (!emitIterator()) // ITER
- return false;
+ if (isAsyncGenerator) {
+ if (!emitAsyncIterator()) // ITER
+ return false;
+ } else {
+ if (!emitIterator()) // ITER
+ return false;
+ }
// Initial send value is undefined.
if (!emit1(JSOP_UNDEFINED)) // ITER RECEIVED
@@ -8674,7 +8762,8 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
//
// If the iterator does not have a "throw" method, it calls IteratorClose
// and then throws a TypeError.
- if (!emitIteratorClose()) // ITER RESULT EXCEPTION
+ IteratorKind iterKind = isAsyncGenerator ? IteratorKind::Async : IteratorKind::Sync;
+ if (!emitIteratorClose(iterKind))
return false;
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw
return false;
@@ -8690,6 +8779,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
if (!emitCall(JSOP_CALL, 1, iter)) // ITER OLDRESULT RESULT
return false;
checkTypeSet(JSOP_CALL);
+
+ if (isAsyncGenerator) {
+ if (!emitAwait()) // ITER OLDRESULT RESULT
+ return false;
+ }
+
if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) // ITER OLDRESULT RESULT
return false;
if (!emit1(JSOP_SWAP)) // ITER RESULT OLDRESULT
@@ -8756,6 +8851,11 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
return false;
checkTypeSet(JSOP_CALL);
+ if (iterKind == IteratorKind::Async) {
+ if (!emitAwait()) // ... FTYPE FVALUE RESULT
+ return false;
+ }
+
// Step v.
if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ITER OLDRESULT FTYPE FVALUE RESULT
return false;
@@ -8830,9 +8930,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter)
return false;
if (!emitCall(JSOP_CALL, 1, iter)) // ITER RESULT
return false;
+ checkTypeSet(JSOP_CALL);
+
+ if (isAsyncGenerator) {
+ if (!emitAwait()) // ITER RESULT RESULT
+ return false;
+ }
+
if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ITER RESULT
return false;
- checkTypeSet(JSOP_CALL);
MOZ_ASSERT(this->stackDepth == startDepth);
if (!emitJumpTargetAndPatch(checkResult)) // checkResult:
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 67e3294890..5eaf5ce146 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -10,6 +10,7 @@
#define frontend_BytecodeEmitter_h
#include "jscntxt.h"
+#include "jsiter.h"
#include "jsopcode.h"
#include "jsscript.h"
@@ -606,6 +607,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitPrepareIteratorResult();
MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
+ MOZ_MUST_USE bool emitToIteratorResult(bool done);
MOZ_MUST_USE bool emitGetDotGenerator();
@@ -613,6 +615,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitYield(ParseNode* pn);
MOZ_MUST_USE bool emitYieldOp(JSOp op);
MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
+ MOZ_MUST_USE bool emitAwait();
MOZ_MUST_USE bool emitAwait(ParseNode* pn);
MOZ_MUST_USE bool emitPropLHS(ParseNode* pn);
@@ -708,10 +711,13 @@ struct MOZ_STACK_CLASS BytecodeEmitter
// It will replace that stack value with the corresponding iterator
MOZ_MUST_USE bool emitIterator();
+ MOZ_MUST_USE bool emitAsyncIterator();
+
// Pops iterator from the top of the stack. Pushes the result of |.next()|
// onto the stack.
MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false);
- MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal,
+ MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync,
+ CompletionKind completionKind = CompletionKind::Normal,
bool allowSelfHosted = false);
template <typename InnerEmitter>
diff --git a/js/src/js.msg b/js/src/js.msg
index 2778085c7e..d22265ba82 100644
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -596,3 +596,5 @@ MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have
// Async Iteration
MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator")
+MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator")
+MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
diff --git a/js/src/jsiter.h b/js/src/jsiter.h
index 17b7df38bf..0b324a0140 100644
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -227,6 +227,8 @@ InitLegacyIteratorClass(JSContext* cx, HandleObject obj);
extern JSObject*
InitStopIterationClass(JSContext* cx, HandleObject obj);
+enum class IteratorKind { Sync, Async };
+
} /* namespace js */
#endif /* jsiter_h */
diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp
index 04effe1b10..74f6389c10 100644
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.cpp
@@ -148,6 +148,69 @@ js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> a
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.
+JSObject*
+js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
+{
+ // Step 1 (implicit).
+ // Done in bytecode emitted by emitAsyncIterator.
+
+ // Steps 2-4.
+ return AsyncFromSyncIteratorObject::create(cx, iter);
+}
+
+// Async Iteration proposal 6.1.3.1 steps 2-4.
+/* static */ JSObject*
+AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
+{
+ // Step 2.
+ RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx,
+ cx->global()));
+ if (!proto)
+ return nullptr;
+
+ RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>();
+
+ // Step 3.
+ asyncIter->setIterator(iter);
+
+ // Step 4.
+ return asyncIter;
+}
+
+// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
+static bool
+AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
+}
+
+// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return.
+static bool
+AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
+}
+
+// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw.
+static bool
+AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
+}
+
// Async Iteration proposal 6.4.1.2 AsyncGenerator.prototype.next.
static bool
AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
@@ -413,17 +476,17 @@ js::AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncG
return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument);
}
-// Async Iteration proposal 6.2.1.2 (partially).
+// Async Iteration proposal 6.2.1.3 (partially).
// Most steps are done in generator.
static MOZ_MUST_USE bool
AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
- HandleValue value, bool done)
+ HandleValue value)
{
- // Step 6.
+ // Step 5.
asyncGenObj->setSuspendedYield();
- // Step 10.c.
- return AsyncGeneratorResolve(cx, asyncGenObj, value, done);
+ // Step 8.
+ return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
}
// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20.
@@ -451,41 +514,30 @@ AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
return AsyncGeneratorThrown(cx, asyncGenObj);
}
+ if (asyncGenObj->generatorObj()->isAfterAwait())
+ return AsyncGeneratorAwait(cx, asyncGenObj, result);
+
// The following code corresponds to the following 3 cases:
// * yield
- // * await
+ // * yield*
// * return
- // For await and return, property access is done on an internal result
+ // For yield and return, property access is done on an internal result
// object and it's not observable.
- // For yield, it's done on an user-provided result object, and it's
- // observable, so perform in that order in all cases.
+ // For yield*, it's done on a possibly user-provided result object, and
+ // it's observable.
+ // 2.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
RootedObject resultObj(cx, &result.toObject());
RootedValue value(cx);
- RootedValue doneVal(cx);
- // 6.2.1.2 step 10.a.
if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
return false;
- // 6.2.1.2 step 10.b.
- if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
- return false;
-
- // 6.2.1.2 step 10.c.
if (asyncGenObj->generatorObj()->isAfterYield())
- return AsyncGeneratorYield(cx, asyncGenObj, value, ToBoolean(doneVal));
+ return AsyncGeneratorYield(cx, asyncGenObj, value);
// 6.4.3.2 step 5.d-g.
- if (ToBoolean(doneVal)) {
- MOZ_ASSERT(!asyncGenObj->generatorObj()->isAfterAwait());
- return AsyncGeneratorReturned(cx, asyncGenObj, value);
- }
-
- MOZ_ASSERT(asyncGenObj->generatorObj()->isAfterAwait());
-
- // 5.1 steps 2-9.
- return AsyncGeneratorAwait(cx, asyncGenObj, value);
+ return AsyncGeneratorReturned(cx, asyncGenObj, value);
}
static const JSFunctionSpec async_iterator_proto_methods[] = {
@@ -493,6 +545,13 @@ static const JSFunctionSpec async_iterator_proto_methods[] = {
JS_FS_END
};
+static const JSFunctionSpec async_from_sync_iter_methods[] = {
+ JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
+ JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
+ JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0),
+ JS_FS_END
+};
+
static const JSFunctionSpec async_generator_methods[] = {
JS_FN("next", AsyncGeneratorNext, 1, 0),
JS_FN("throw", AsyncGeneratorThrow, 1, 0),
@@ -513,6 +572,19 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
return false;
+ // Async Iteration proposal 6.1.3.2 %AsyncFromSyncIteratorPrototype%.
+ RootedObject asyncFromSyncIterProto(
+ cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+ asyncIterProto));
+ if (!asyncFromSyncIterProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
+ async_from_sync_iter_methods) ||
+ !DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator))
+ {
+ return false;
+ }
+
// Async Iteration proposal 6.4.1 %AsyncGeneratorPrototype%.
RootedObject asyncGenProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
@@ -557,6 +629,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
}
global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
+ global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto));
global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator));
global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction));
global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto));
diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h
index c0135283fa..974c209a01 100644
--- a/js/src/vm/AsyncIteration.h
+++ b/js/src/vm/AsyncIteration.h
@@ -196,6 +196,32 @@ class AsyncGeneratorObject : public NativeObject
}
};
+JSObject*
+CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter);
+
+class AsyncFromSyncIteratorObject : public NativeObject
+{
+ private:
+ enum AsyncFromSyncIteratorObjectSlots {
+ Slot_Iterator = 0,
+ Slots
+ };
+
+ void setIterator(HandleObject iterator_) {
+ setFixedSlot(Slot_Iterator, ObjectValue(*iterator_));
+ }
+
+ public:
+ static const Class class_;
+
+ static JSObject*
+ create(JSContext* cx, HandleObject iter);
+
+ JSObject* iterator() const {
+ return &getFixedSlot(Slot_Iterator).toObject();
+ }
+};
+
MOZ_MUST_USE bool
AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index 4a9c03e884..6a8afb56b5 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -29,6 +29,7 @@
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(as, as, "as") \
macro(Async, Async, "Async") \
+ macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \
macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \
macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
index ce802daba9..3fd2762f84 100644
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -101,6 +101,7 @@ class GlobalObject : public NativeObject
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
ASYNC_ITERATOR_PROTO,
+ ASYNC_FROM_SYNC_ITERATOR_PROTO,
ASYNC_GENERATOR,
ASYNC_GENERATOR_FUNCTION,
ASYNC_GENERATOR_PROTO,
@@ -636,6 +637,12 @@ class GlobalObject : public NativeObject
}
static NativeObject*
+ getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
+ static NativeObject*
getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR,
initAsyncGenerators));
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 4d59273243..3cf9b57f60 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1940,7 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode)
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
CASE(JSOP_UNUSED126)
-CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
CASE(JSOP_UNUSED221)
@@ -3596,6 +3595,17 @@ CASE(JSOP_TOASYNCGEN)
}
END_CASE(JSOP_TOASYNCGEN)
+CASE(JSOP_TOASYNCITER)
+{
+ ReservedRooted<JSObject*> iter(&rootObject1, &REGS.sp[-1].toObject());
+ JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter);
+ if (!asyncIter)
+ goto error;
+
+ REGS.sp[-1].setObject(*asyncIter);
+}
+END_CASE(JSOP_TOASYNCITER)
+
CASE(JSOP_SETFUNNAME)
{
MOZ_ASSERT(REGS.stackDepth() >= 2);
@@ -5138,6 +5148,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
case CheckIsObjectKind::GetIterator:
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
break;
+ case CheckIsObjectKind::GetAsyncIterator:
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE);
+ break;
default:
MOZ_CRASH("Unknown kind");
}
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
index 9fefd75ccd..5e7087f7fa 100644
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -561,7 +561,8 @@ enum class CheckIsObjectKind : uint8_t {
IteratorNext,
IteratorReturn,
IteratorThrow,
- GetIterator
+ GetIterator,
+ GetAsyncIterator
};
bool
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index fc7fbbe662..e13cd62219 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2136,7 +2136,15 @@
* Stack: promise, gen => resolved
*/ \
macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \
- macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Pops the iterator from the top of the stack, and create async iterator
+ * from it and push the async iterator back onto the stack.
+ * Category: Statements
+ * Type: Generator
+ * Operands:
+ * Stack: iter => asynciter
+ */ \
+ macro(JSOP_TOASYNCITER, 210, "toasynciter", NULL, 1, 1, 1, JOF_BYTE) \
macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Initializes generator frame, creates a generator and pushes it on the