Bug 1343481 - Part 2: Stop using StarGegerator for async function. r=shu
authorTooru Fujisawa <arai_a@mac.com>
Wed, 01 Mar 2017 20:40:04 +0900
changeset 345291 5ce3e1a949a61ab7a5caf942e8ca318621babc68
parent 345290 3f9cb5adce0e76c382b910b52e602d552b3dc1b9
child 345292 53a92e2f749e1263e72e0d38089758948d5d720a
push id31436
push userkwierso@gmail.com
push dateThu, 02 Mar 2017 01:18:52 +0000
treeherdermozilla-central@e91de6fb2b3d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1343481
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1343481 - Part 2: Stop using StarGegerator for async function. r=shu
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsscript.cpp
js/src/vm/AsyncFunction.cpp
js/src/vm/Debugger.cpp
js/src/vm/GeneratorObject.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -747,10 +747,10 @@ bool
 frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
                                          const ReadOnlyCompileOptions& options,
                                          JS::SourceBufferHolder& srcBuf,
                                          const Maybe<uint32_t>& parameterListEnd)
 {
     RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope());
 
     BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope);
-    return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd);
+    return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -8241,32 +8241,33 @@ BytecodeEmitter::emitCheckDerivedClassCo
 }
 
 bool
 BytecodeEmitter::emitReturn(ParseNode* pn)
 {
     if (!updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
-    if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) {
+    bool needsIteratorResult = sc->isFunctionBox() && sc->asFunctionBox()->needsIteratorResult();
+    if (needsIteratorResult) {
         if (!emitPrepareIteratorResult())
             return false;
     }
 
     /* Push a return value */
     if (ParseNode* pn2 = pn->pn_kid) {
         if (!emitTree(pn2))
             return false;
     } else {
         /* No explicit return value provided */
         if (!emit1(JSOP_UNDEFINED))
             return false;
     }
 
-    if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) {
+    if (needsIteratorResult) {
         if (!emitFinishIteratorResult(true))
             return false;
     }
 
     // We know functionBodyEndPos is set because "return" is only
     // valid in a function, and so we've passed through
     // emitFunctionScript.
     MOZ_ASSERT(functionBodyEndPosSet);
@@ -8328,28 +8329,29 @@ BytecodeEmitter::emitReturn(ParseNode* p
 }
 
 bool
 BytecodeEmitter::emitYield(ParseNode* pn)
 {
     MOZ_ASSERT(sc->isFunctionBox());
 
     if (pn->getOp() == JSOP_YIELD) {
-        if (sc->asFunctionBox()->isStarGenerator()) {
+        bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult();
+        if (needsIteratorResult) {
             if (!emitPrepareIteratorResult())
                 return false;
         }
         if (pn->pn_left) {
             if (!emitTree(pn->pn_left))
                 return false;
         } else {
             if (!emit1(JSOP_UNDEFINED))
                 return false;
         }
-        if (sc->asFunctionBox()->isStarGenerator()) {
+        if (needsIteratorResult) {
             if (!emitFinishIteratorResult(false))
                 return false;
         }
     } else {
         MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD);
     }
 
     if (!emitTree(pn->pn_right))
@@ -10092,24 +10094,29 @@ BytecodeEmitter::emitFunctionBody(ParseN
 {
     FunctionBox* funbox = sc->asFunctionBox();
 
     if (!emitTree(funBody))
         return false;
 
     if (funbox->needsFinalYield()) {
         // If we fall off the end of a generator, do a final yield.
-        if (funbox->isStarGenerator() && !emitPrepareIteratorResult())
-            return false;
+        bool needsIteratorResult = funbox->needsIteratorResult();
+        if (needsIteratorResult) {
+            if (!emitPrepareIteratorResult())
+                return false;
+        }
 
         if (!emit1(JSOP_UNDEFINED))
             return false;
 
-        if (funbox->isStarGenerator() && !emitFinishIteratorResult(true))
-            return false;
+        if (needsIteratorResult) {
+            if (!emitFinishIteratorResult(true))
+                return false;
+        }
 
         if (!emit1(JSOP_SETRVAL))
             return false;
 
         NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator);
         if (!emitGetNameAtLocation(cx->names().dotGenerator, loc))
             return false;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2414,20 +2414,18 @@ Parser<SyntaxParseHandler>::finishFuncti
     // parse.
     PropagateTransitiveParseFlags(funbox, lazy);
 
     fun->initLazyScript(lazy);
     return true;
 }
 
 static YieldHandling
-GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind)
-{
-    if (asyncKind == AsyncFunction)
-        return YieldIsName;
+GetYieldHandling(GeneratorKind generatorKind)
+{
     if (generatorKind == NotGenerator)
         return YieldIsName;
     return YieldIsKeyword;
 }
 
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
@@ -2455,17 +2453,17 @@ Parser<FullParseHandler>::standaloneFunc
         return null();
     funbox->initStandaloneFunction(enclosingScope);
 
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
-    YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+    YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     TokenKind tt;
@@ -2614,34 +2612,34 @@ Parser<ParseHandler>::functionBody(InHan
         if (pc->isAsync()) {
             handler.addStatementToList(stmtList, pn);
             pn = stmtList;
         }
     }
 
     switch (pc->generatorKind()) {
       case NotGenerator:
-        MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
+        MOZ_ASSERT_IF(!pc->isAsync(), pc->lastYieldOffset == startYieldOffset);
         break;
 
       case LegacyGenerator:
         MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
 
         // These should throw while parsing the yield expression.
         MOZ_ASSERT(kind != Arrow);
         MOZ_ASSERT(!IsGetterKind(kind));
         MOZ_ASSERT(!IsSetterKind(kind));
         MOZ_ASSERT(!IsConstructorKind(kind));
         MOZ_ASSERT(kind != Method);
         MOZ_ASSERT(type != ExpressionBody);
         break;
 
       case StarGenerator:
-        MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
-        MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
+        MOZ_ASSERT(kind != Arrow);
+        MOZ_ASSERT(type == StatementListBody);
         break;
     }
 
     if (pc->needsDotGeneratorName()) {
         MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
         if (!declareDotGeneratorName())
             return null();
         Node generator = newDotGeneratorName();
@@ -2676,29 +2674,29 @@ Parser<ParseHandler>::newFunction(Handle
 
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
     JSFunction::Flags flags;
 #ifdef DEBUG
     bool isGlobalSelfHostedBuiltin = false;
 #endif
     switch (kind) {
       case Expression:
-        flags = (generatorKind == NotGenerator
+        flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
                  ? JSFunction::INTERPRETED_LAMBDA
-                 : JSFunction::INTERPRETED_LAMBDA_GENERATOR);
+                 : JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC);
         break;
       case Arrow:
         flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Method:
         MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
-        flags = (generatorKind == NotGenerator
+        flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
                  ? JSFunction::INTERPRETED_METHOD
-                 : JSFunction::INTERPRETED_METHOD_GENERATOR);
+                 : JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC);
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case ClassConstructor:
       case DerivedClassConstructor:
         flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         break;
       case Getter:
@@ -2714,19 +2712,19 @@ Parser<ParseHandler>::newFunction(Handle
       default:
         MOZ_ASSERT(kind == Statement);
 #ifdef DEBUG
         if (options().selfHostingMode && !pc->isFunctionBox()) {
             isGlobalSelfHostedBuiltin = true;
             allocKind = gc::AllocKind::FUNCTION_EXTENDED;
         }
 #endif
-        flags = (generatorKind == NotGenerator
+        flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
                  ? JSFunction::INTERPRETED_NORMAL
-                 : JSFunction::INTERPRETED_GENERATOR);
+                 : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC);
     }
 
     // We store the async wrapper in a slot for later access.
     if (asyncKind == AsyncFunction)
         allocKind = gc::AllocKind::FUNCTION_EXTENDED;
 
     fun = NewFunctionWithProto(context, nullptr, 0, flags, nullptr, atom, proto,
                                allocKind, TenuredObject);
@@ -3239,29 +3237,28 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDefinition(Node pn, InHandling inHandling,
                                          YieldHandling yieldHandling, HandleAtom funName,
                                          FunctionSyntaxKind kind,
                                          GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
                                          bool tryAnnexB /* = false */)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
-    MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator);
 
     // When fully parsing a LazyScript, we do not fully reparse its inner
     // functions, which are also lazy. Instead, their free variables and
     // source extents are recorded and may be skipped.
     if (handler.canSkipLazyInnerFunctions()) {
         if (!skipLazyInnerFunction(pn, kind, tryAnnexB))
             return null();
         return pn;
     }
 
     RootedObject proto(context);
-    if (generatorKind == StarGenerator) {
+    if (generatorKind == StarGenerator || asyncKind == AsyncFunction) {
         // If we are off thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseTask, so cx will not
         // be necessary.
         JSContext* cx = context->helperThread() ? nullptr : context;
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
@@ -3321,17 +3318,17 @@ Parser<FullParseHandler>::trySyntaxParse
                                                       Directives* newDirectives)
 {
     // Try a syntax parse for this inner function.
     do {
         // If we're assuming this function is an IIFE, always perform a full
         // parse to avoid the overhead of a lazy syntax-only parse. Although
         // the prediction may be incorrect, IIFEs are common enough that it
         // pays off for lots of code.
-        if (pn->isLikelyIIFE() && generatorKind == NotGenerator)
+        if (pn->isLikelyIIFE() && generatorKind == NotGenerator && asyncKind == SyncFunction)
             break;
 
         Parser<SyntaxParseHandler>* parser = handler.syntaxParser;
         if (!parser)
             break;
 
         UsedNameTracker::RewindToken token = usedNames.getRewindToken();
 
@@ -3493,17 +3490,17 @@ Parser<FullParseHandler>::standaloneLazy
     // is a not-async arrow, use TokenStream::Operand to keep
     // verifyConsistentModifier from complaining (we will use
     // TokenStream::Operand in functionArguments).
     TokenStream::Modifier modifier = (fun->isArrow() && asyncKind == SyncFunction)
                                      ? TokenStream::Operand : TokenStream::None;
     if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier))
         return null();
 
-    YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+    YieldHandling yieldHandling = GetYieldHandling(generatorKind);
     FunctionSyntaxKind syntaxKind = Statement;
     if (fun->isClassConstructor())
         syntaxKind = ClassConstructor;
     else if (fun->isMethod())
         syntaxKind = Method;
     else if (fun->isGetter())
         syntaxKind = Getter;
     else if (fun->isSetter())
@@ -3568,17 +3565,17 @@ Parser<ParseHandler>::functionFormalPara
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_LC) {
-        if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
+        if (funbox->isStarGenerator() || kind == Method ||
             kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
             IsConstructorKind(kind)) {
             error(JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow) {
 #if JS_HAS_EXPR_CLOSURES
@@ -3596,17 +3593,17 @@ Parser<ParseHandler>::functionFormalPara
         funbox->setIsExprBody();
     }
 
     // Arrow function parameters inherit yieldHandling from the enclosing
     // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|,
     // |yield| in the parameters is either a name or keyword, depending on
     // whether the arrow function is enclosed in a generator function or not.
     // Whereas the |yield| in the function body is always parsed as a name.
-    YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind());
+    YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     Node body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
     if (!body)
         return false;
 
     if ((kind != Method && !IsConstructorKind(kind)) && fun->explicitName()) {
         RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
         // `await` cannot be checked at this point because of different context.
         // It should already be checked before this point.
@@ -3688,17 +3685,17 @@ Parser<ParseHandler>::functionStmt(Yield
             return null();
         }
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
-    GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
+    GeneratorKind generatorKind = NotGenerator;
     if (tt == TOK_MUL) {
         if (asyncKind != SyncFunction) {
             error(JSMSG_ASYNC_GENERATOR);
             return null();
         }
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
@@ -3719,17 +3716,17 @@ Parser<ParseHandler>::functionStmt(Yield
     }
 
     // Note the declared name and check for early errors.
     bool tryAnnexB = false;
     if (declaredInStmt) {
         MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label);
         MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
 
-        if (!pc->sc()->strict() && generatorKind == NotGenerator) {
+        if (!pc->sc()->strict() && generatorKind == NotGenerator && asyncKind == SyncFunction) {
             // Under sloppy mode, try Annex B.3.3 semantics. If making an
             // additional 'var' binding of the same name does not throw an
             // early error, do so. This 'var' binding would be assigned
             // the function object when its declaration is reached, not at
             // the start of the block.
             if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
                 return null();
         }
@@ -3743,44 +3740,44 @@ Parser<ParseHandler>::functionStmt(Yield
         if (!noteDeclaredName(name, kind, pos()))
             return null();
     }
 
     Node pn = handler.newFunctionStatement();
     if (!pn)
         return null();
 
-    YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind);
+    YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
     return functionDefinition(pn, InAllowed, newYieldHandling, name, Statement, generatorKind,
                               asyncKind, tryAnnexB);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
-    GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
+    GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
         if (asyncKind != SyncFunction) {
             error(JSMSG_ASYNC_GENERATOR);
             return null();
         }
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
-    YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+    YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
     RootedPropertyName name(context);
     if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else {
         tokenStream.ungetToken();
@@ -7996,40 +7993,38 @@ Parser<ParseHandler>::assignExpr(InHandl
         if (next == TOK_LC)
             isBlock = true;
 
         tokenStream.seek(start);
 
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
-        GeneratorKind generatorKind = NotGenerator;
         FunctionAsyncKind asyncKind = SyncFunction;
 
         if (next == TOK_ASYNC) {
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
             TokenKind nextSameLine = TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
 
             if (nextSameLine == TOK_ARROW) {
                 tokenStream.ungetToken();
             } else {
-                generatorKind = StarGenerator;
                 asyncKind = AsyncFunction;
             }
         }
 
         Node pn = handler.newArrowFunction();
         if (!pn)
             return null();
 
         Node arrowFunc = functionDefinition(pn, inHandling, yieldHandling, nullptr,
-                                            Arrow, generatorKind, asyncKind);
+                                            Arrow, NotGenerator, asyncKind);
         if (!arrowFunc)
             return null();
 
         if (isBlock) {
             // This arrow function could be a non-trailing member of a comma
             // expression or a semicolon terminating a full expression.  If so,
             // the next token is that comma/semicolon, gotten with None:
             //
@@ -9626,26 +9621,25 @@ Parser<ParseHandler>::methodDefinition(P
       case PropertyType::DerivedConstructor:
         kind = DerivedClassConstructor;
         break;
 
       default:
         MOZ_CRASH("unexpected property type");
     }
 
-    GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod ||
-                                   propType == PropertyType::AsyncMethod)
+    GeneratorKind generatorKind = propType == PropertyType::GeneratorMethod
                                   ? StarGenerator
                                   : NotGenerator;
 
     FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod)
                                   ? AsyncFunction
                                   : SyncFunction;
 
-    YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+    YieldHandling yieldHandling = GetYieldHandling(generatorKind);
 
     Node pn = handler.newFunctionExpression();
     if (!pn)
         return null();
 
     return functionDefinition(pn, InAllowed, yieldHandling, funName, kind, generatorKind, asyncKind);
 }
 
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -543,16 +543,19 @@ class FunctionBox : public ObjectBox, pu
     FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); }
 
     bool needsFinalYield() const {
         return isStarGenerator() || isLegacyGenerator() || isAsync();
     }
     bool needsDotGeneratorName() const {
         return isStarGenerator() || isLegacyGenerator() || isAsync();
     }
+    bool needsIteratorResult() const {
+        return isStarGenerator() || isAsync();
+    }
 
     bool isAsync() const { return asyncKind() == AsyncFunction; }
     bool isArrow() const { return function()->isArrow(); }
 
     bool hasRest() const { return hasRest_; }
     void setHasRest() {
         hasRest_ = true;
     }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -556,17 +556,17 @@ fun_resolve(JSContext* cx, HandleObject 
 
 template<XDRMode mode>
 bool
 js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
                            HandleScriptSource sourceObject, MutableHandleFunction objp)
 {
     enum FirstWordFlag {
         HasAtom             = 0x1,
-        IsStarGenerator     = 0x2,
+        HasStarGeneratorProto = 0x2,
         IsLazy              = 0x4,
         HasSingletonType    = 0x8
     };
 
     /* NB: Keep this in sync with CloneInnerInterpretedFunction. */
     RootedAtom atom(xdr->cx());
     uint32_t firstword = 0;        /* bitmask of FirstWordFlag */
     uint32_t flagsword = 0;        /* word for argument count and fun->flags */
@@ -579,18 +579,18 @@ js::XDRInterpretedFunction(XDRState<mode
     if (mode == XDR_ENCODE) {
         fun = objp;
         if (!fun->isInterpreted())
             return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
 
         if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
             firstword |= HasAtom;
 
-        if (fun->isStarGenerator())
-            firstword |= IsStarGenerator;
+        if (fun->isStarGenerator() || fun->isAsync())
+            firstword |= HasStarGeneratorProto;
 
         if (fun->isInterpretedLazy()) {
             // Encode a lazy script.
             firstword |= IsLazy;
             lazy = fun->lazyScript();
         } else {
             // Encode the script.
             script = fun->nonLazyScript();
@@ -620,17 +620,17 @@ js::XDRInterpretedFunction(XDRState<mode
 
     if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
         return false;
     if (!xdr->codeUint32(&flagsword))
         return false;
 
     if (mode == XDR_DECODE) {
         RootedObject proto(cx);
-        if (firstword & IsStarGenerator) {
+        if (firstword & HasStarGeneratorProto) {
             // If we are off thread, the generator meta-objects have
             // already been created by js::StartOffThreadParseTask, so
             // JSContext* will not be necessary.
             JSContext* context = cx->helperThread() ? nullptr : cx;
             proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(context, cx->global());
             if (!proto)
                 return false;
         }
@@ -1006,17 +1006,17 @@ js::FunctionToString(JSContext* cx, Hand
             return nullptr;
     }
     if (fun->isAsync()) {
         if (!out.append("async "))
             return nullptr;
     }
     if (!fun->isArrow()) {
         bool ok;
-        if (fun->isStarGenerator() && !fun->isAsync())
+        if (fun->isStarGenerator())
             ok = out.append("function* ");
         else
             ok = out.append("function ");
         if (!ok)
             return nullptr;
     }
     if (fun->explicitName()) {
         if (!out.append(fun->explicitName()))
@@ -1604,18 +1604,16 @@ FunctionConstructor(JSContext* cx, const
     if (!GlobalObject::isRuntimeCodeGenEnabled(cx, global)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_FUNCTION);
         return false;
     }
 
     bool isStarGenerator = generatorKind == StarGenerator;
     bool isAsync = asyncKind == AsyncFunction;
     MOZ_ASSERT(generatorKind != LegacyGenerator);
-    MOZ_ASSERT_IF(isAsync, isStarGenerator);
-    MOZ_ASSERT_IF(!isStarGenerator, !isAsync);
 
     RootedScript maybeScript(cx);
     const char* filename;
     unsigned lineno;
     bool mutedErrors;
     uint32_t pcOffset;
     DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                          &mutedErrors);
@@ -1702,17 +1700,17 @@ FunctionConstructor(JSContext* cx, const
     RootedObject proto(cx);
     if (!isAsync) {
         if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
             return false;
     }
 
     // Step 4.d, use %Generator% as the fallback prototype.
     // Also use %Generator% for the unwrapped function of async functions.
-    if (!proto && isStarGenerator) {
+    if (!proto && (isStarGenerator || isAsync)) {
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global);
         if (!proto)
             return false;
     }
 
     // Step 25-32 (reordered).
     RootedObject globalLexical(cx, &global->lexicalEnvironment());
     AllocKind allocKind = isAsync ? AllocKind::FUNCTION_EXTENDED : AllocKind::FUNCTION;
@@ -1770,17 +1768,17 @@ js::AsyncFunctionConstructor(JSContext* 
 
     // Save the callee before its reset in FunctionConstructor().
     RootedObject newTarget(cx);
     if (args.isConstructing())
         newTarget = &args.newTarget().toObject();
     else
         newTarget = &args.callee();
 
-    if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
+    if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction))
         return false;
 
     // ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077
     // 19.2.1.1.1 Runtime Semantics: CreateDynamicFunction, step 24.
     RootedObject proto(cx);
     if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
         return false;
 
@@ -1966,17 +1964,17 @@ js::CanReuseScriptForClone(JSCompartment
            (fun->hasScript() && fun->nonLazyScript()->hasNonSyntacticScope());
 }
 
 static inline JSFunction*
 NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind,
                  gc::AllocKind allocKind, HandleObject proto)
 {
     RootedObject cloneProto(cx, proto);
-    if (!proto && fun->isStarGenerator()) {
+    if (!proto && (fun->isStarGenerator() || fun->isAsync())) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
 
     JSObject* cloneobj = NewObjectWithClassProto(cx, &JSFunction::class_, cloneProto,
                                                  allocKind, newKind);
     if (!cloneobj)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -84,25 +84,25 @@ class JSFunction : public js::NativeObje
 
         /* Derived Flags values for convenience: */
         NATIVE_FUN = 0,
         NATIVE_CTOR = NATIVE_FUN | CONSTRUCTOR,
         NATIVE_CLASS_CTOR = NATIVE_FUN | CONSTRUCTOR | CLASSCONSTRUCTOR_KIND,
         ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR,
         ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA,
         INTERPRETED_METHOD = INTERPRETED | METHOD_KIND,
-        INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND,
+        INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND,
         INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR,
         INTERPRETED_GETTER = INTERPRETED | GETTER_KIND,
         INTERPRETED_SETTER = INTERPRETED | SETTER_KIND,
         INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR,
         INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND,
-        INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA,
+        INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = INTERPRETED | LAMBDA,
         INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR,
-        INTERPRETED_GENERATOR = INTERPRETED,
+        INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED,
         NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
 
         STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA |
                                SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK
     };
 
     static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
                   "jsfriendapi.h's JSFunction::INTERPRETED-alike is wrong");
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3219,17 +3219,17 @@ Rebase(JSScript* dst, JSScript* src, T* 
     return reinterpret_cast<T*>(dst->data + off);
 }
 
 static JSObject*
 CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
     RootedObject cloneProto(cx);
-    if (srcFun->isStarGenerator()) {
+    if (srcFun->isStarGenerator() || srcFun->isAsync()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
 
     gc::AllocKind allocKind = srcFun->getAllocKind();
     uint16_t flags = srcFun->flags();
     if (srcFun->isSelfHostedBuiltin()) {
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -107,17 +107,17 @@ WrappedAsyncFunction(JSContext* cx, unsi
 // Async Functions proposal 2.1 steps 1, 3 (partially).
 // In the spec it creates a function, but we create 2 functions `unwrapped` and
 // `wrapped`.  `unwrapped` is a generator that corresponds to
 //  the async function's body, replacing `await` with `yield`.  `wrapped` is a
 // function that is visible to the outside, and handles yielded values.
 JSObject*
 js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
 {
-    MOZ_ASSERT(unwrapped->isStarGenerator());
+    MOZ_ASSERT(unwrapped->isAsync());
     MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
                       "%FunctionPrototype% fallback in NewFunctionWithProto().");
 
     // Create a new function with AsyncFunctionPrototype, reusing the name and
     // the length of `unwrapped`.
 
     RootedAtom funName(cx, unwrapped->explicitName());
     uint16_t length;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1603,17 +1603,17 @@ CheckResumptionValue(JSContext* cx, Abst
         // async function violate the iterator protocol. The return value from
         // such a frame must have the form { done: <bool>, value: <anything> }.
         RootedFunction callee(cx, frame.callee());
         if (callee->isAsync()) {
             if (!CheckAsyncResumptionValue(cx, vp)) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT);
                 return false;
             }
-        } else if (callee->isStarGenerator()) {
+        } else if (callee->isStarGenerator() || callee->isAsync()) {
             if (!CheckStarGeneratorResumptionValue(cx, vp)) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
                 return false;
             }
         }
     }
 
     if (maybeThisv.isSome()) {
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -20,17 +20,17 @@ JSObject*
 GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
 {
     MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() ||
                frame.script()->isAsync());
     MOZ_ASSERT(frame.script()->nfixed() == 0);
 
     Rooted<GlobalObject*> global(cx, cx->global());
     RootedNativeObject obj(cx);
-    if (frame.script()->isStarGenerator()) {
+    if (frame.script()->isStarGenerator() || frame.script()->isAsync()) {
         RootedValue pval(cx);
         RootedObject fun(cx, frame.callee());
         // FIXME: This would be faster if we could avoid doing a lookup to get
         // the prototype for the instance.  Bug 906600.
         if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval))
             return nullptr;
         RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
         if (!proto) {