From 5bdfdb678d4063f77392d5029b9803c8cdc6f248 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Dec 17 2019 11:25:28 +0000 Subject: Bug 1331092 - Part 9: Implement for-await-of. Tag #1287 --- diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index d926e13..36495bd 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2029,12 +2029,16 @@ class ForOfLoopControl : public LoopControl bool allowSelfHosted_; + IteratorKind iterKind_; + public: - ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted) + ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted, + IteratorKind iterKind) : LoopControl(bce, StatementKind::ForOfLoop), iterDepth_(iterDepth), numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX), - allowSelfHosted_(allowSelfHosted) + allowSelfHosted_(allowSelfHosted), + iterKind_(iterKind) { } @@ -2118,7 +2122,7 @@ class ForOfLoopControl : public LoopControl bool emitIteratorClose(BytecodeEmitter* bce, CompletionKind completionKind = CompletionKind::Normal) { ptrdiff_t start = bce->offset(); - if (!bce->emitIteratorClose(IteratorKind::Sync, completionKind, allowSelfHosted_)) + if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_)) return false; ptrdiff_t end = bce->offset(); return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); @@ -5246,7 +5250,8 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri } bool -BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false */) +BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */, + bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " @@ -5260,6 +5265,12 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false return false; if (!emitCall(JSOP_CALL, 0, pn)) // ... RESULT return false; + + if (iterKind == IteratorKind::Async) { + if (!emitAwait()) // ... RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT return false; checkTypeSet(JSOP_CALL); @@ -7099,7 +7110,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted) if (!emitDupAt(2)) // ITER ARR I ITER return false; - if (!emitIteratorNext(nullptr, allowSelfHosted)) // ITER ARR I RESULT + if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted)) // ITER ARR I RESULT return false; if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT return false; @@ -7199,6 +7210,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte MOZ_ASSERT(forOfHead->isKind(PNK_FOROF)); MOZ_ASSERT(forOfHead->isArity(PN_TERNARY)); + unsigned iflags = forOfLoop->pn_iflags; + IteratorKind iterKind = (iflags & JSITER_FORAWAITOF) + ? IteratorKind::Async + : IteratorKind::Sync; + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()); + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync()); + ParseNode* forHeadExpr = forOfHead->pn_kid3; // Certain builtins (e.g. Array.from) are implemented in self-hosting @@ -7214,8 +7232,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte // Evaluate the expression being iterated. if (!emitTree(forHeadExpr)) // ITERABLE return false; - if (!emitIterator()) // ITER - return false; + if (iterKind == IteratorKind::Async) { + if (!emitAsyncIterator()) // ITER + return false; + } else { + if (!emitIterator()) // ITER + return false; + } int32_t iterDepth = stackDepth; @@ -7226,7 +7249,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF return false; - ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter); + ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind); // Annotate so IonMonkey can find the loop-closing jump. unsigned noteIndex; @@ -7322,7 +7345,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emitDupAt(1)) // ITER UNDEF ITER return false; - if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER UNDEF RESULT + if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT return false; if (!emit1(JSOP_SWAP)) // ITER RESULT UNDEF diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 5eaf5ce..47bc87d 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -715,7 +715,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter // 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 emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync, + bool allowSelfHosted = false); MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync, CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7704cf6..2d2f17f 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5972,6 +5972,7 @@ Parser::matchInOrOf(bool* isForInp, bool* isForOfp) template bool Parser::forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, Maybe& forLoopLexicalScope, @@ -6062,6 +6063,11 @@ Parser::forHeadStart(YieldHandling yieldHandling, if (!matchInOrOf(&isForIn, &isForOf)) return false; + if (iterKind == IteratorKind::Async && !isForOf) { + error(JSMSG_FOR_AWAIT_NOT_OF); + return false; + } + // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled // the init expression; the caller handles the rest. Allow the Operand // modifier when regetting: Operand must be used to examine the ';' in @@ -6135,6 +6141,7 @@ Parser::forStatement(YieldHandling yieldHandling) ParseContext::Statement stmt(pc, StatementKind::ForLoop); bool isForEach = false; + IteratorKind iterKind = IteratorKind::Sync; unsigned iflags = 0; if (allowsForEachIn()) { @@ -6149,6 +6156,19 @@ Parser::forStatement(YieldHandling yieldHandling) } } +#ifndef RELEASE_OR_BETA + if (pc->isAsync()) { + bool matched; + if (!tokenStream.matchToken(&matched, TOK_AWAIT)) + return null(); + + if (matched) { + iflags |= JSITER_FORAWAITOF; + iterKind = IteratorKind::Async; + } + } +#endif + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type. @@ -6186,7 +6206,7 @@ Parser::forStatement(YieldHandling yieldHandling) // // In either case the subsequent token can be consistently accessed using // TokenStream::None semantics. - if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope, + if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope, &iteratedExpr)) { return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 73c2817..243a770 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -12,6 +12,7 @@ #include "mozilla/Array.h" #include "mozilla/Maybe.h" +#include "jsiter.h" #include "jspubtd.h" #include "frontend/BytecodeCompiler.h" @@ -1196,6 +1197,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node forStatement(YieldHandling yieldHandling); bool forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, mozilla::Maybe& forLetImpliedScope, diff --git a/js/src/js.msg b/js/src/js.msg index d22265b..f8e5358 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -595,6 +595,7 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method") // Async Iteration +MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'") 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/jsfriendapi.h b/js/src/jsfriendapi.h index 4912154..7b59689 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -948,6 +948,7 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); #define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ #define JSITER_SYMBOLS 0x20 /* also include symbol property keys */ #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */ +#define JSITER_FORAWAITOF 0x80 /* for-await-of */ JS_FRIEND_API(bool) RunningWithTrustedPrincipals(JSContext* cx);