From ef44324d916b89b95fa0ea77a3d91eafb4359bf8 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:16:32 -0500 Subject: Bug 1331092 - Part 7: Implement Async Generator yield*. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 130 ++++++++++++++++++++++++++++++++---- js/src/frontend/BytecodeEmitter.h | 8 ++- 2 files changed, 125 insertions(+), 13 deletions(-) (limited to 'js/src/frontend') diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 9d6a68609..d926e130c 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); @@ -3721,6 +3721,18 @@ BytecodeEmitter::emitFinishIteratorResult(bool done) return true; } +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) { @@ -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 @@ -6971,6 +6989,63 @@ BytecodeEmitter::emitIterator() return true; } +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) { @@ -8592,6 +8667,16 @@ BytecodeEmitter::emitYield(ParseNode* pn) return true; } +bool +BytecodeEmitter::emitAwait() +{ + if (!emitGetDotGenerator()) + return false; + if (!emitYieldOp(JSOP_AWAIT)) + return false; + return true; +} + bool BytecodeEmitter::emitAwait(ParseNode* pn) { @@ -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 67e329489..5eaf5ce14 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 -- cgit v1.2.3