Bug 1478910 - Use JSMSG_AWAIT_IN_DEFAULT error for incomplete await expr in async function/generator parameter. r=arai
authorThi Huynh <so61pi.re@gmail.com>
Tue, 14 Aug 2018 11:06:50 +0000
changeset 431413 1283ee4cfb6bea6641102cd08931540688e4d2cc
parent 431412 5312e71473c402f520db9e24f5c7e5eb6795cbcf
child 431414 98d17facfe0093c4120cedd656d82b46822c1964
push id34442
push useraiakab@mozilla.com
push dateTue, 14 Aug 2018 21:59:35 +0000
treeherdermozilla-central@fa2f3d22c156 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1478910
milestone63.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 1478910 - Use JSMSG_AWAIT_IN_DEFAULT error for incomplete await expr in async function/generator parameter. r=arai Differential Revision: https://phabricator.services.mozilla.com/D3207
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -843,16 +843,17 @@ ParserBase::ParserBase(JSContext* cx, Li
     sourceObject(cx, sourceObject),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     isUnexpectedEOF_(false),
     awaitHandling_(AwaitIsName),
+    inParametersOfAsyncFunction_(false),
     parseGoal_(uint8_t(parseGoal))
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 bool
 ParserBase::checkOptions()
@@ -927,16 +928,39 @@ Parser<FullParseHandler, CharT>::setAwai
 
 template <class ParseHandler, typename CharT>
 inline void
 GeneralParser<ParseHandler, CharT>::setAwaitHandling(AwaitHandling awaitHandling)
 {
     asFinalParser()->setAwaitHandling(awaitHandling);
 }
 
+template <typename CharT>
+void
+Parser<SyntaxParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    this->inParametersOfAsyncFunction_ = inParameters;
+}
+
+template <typename CharT>
+void
+Parser<FullParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    this->inParametersOfAsyncFunction_ = inParameters;
+    if (SyntaxParser* syntaxParser = getSyntaxParser())
+        syntaxParser->setInParametersOfAsyncFunction(inParameters);
+}
+
+template <class ParseHandler, typename CharT>
+inline void
+GeneralParser<ParseHandler, CharT>::setInParametersOfAsyncFunction(bool inParameters)
+{
+    asFinalParser()->setInParametersOfAsyncFunction(inParameters);
+}
+
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
@@ -3817,16 +3841,17 @@ GeneralParser<ParseHandler, CharT>::func
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
         AwaitHandling awaitHandling =
             (funbox->isAsync() || (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword()))
             ? AwaitIsKeyword
             : AwaitIsName;
         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, awaitHandling);
+        AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, funbox->isAsync());
         if (!functionArguments(yieldHandling, kind, *pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3878,16 +3903,17 @@ GeneralParser<ParseHandler, CharT>::func
     // Whereas the |yield| in the function body is always parsed as a name.
     // The same goes when parsing |await| in arrow functions.
     YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind());
     AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind());
     bool inheritedStrict = pc->sc()->strict();
     Node body;
     {
         AutoAwaitIsKeyword<ParseHandler, CharT> awaitIsKeyword(this, bodyAwaitHandling);
+        AutoInParametersOfAsyncFunction<ParseHandler, CharT> inParameters(this, false);
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     // Revalidate the function name when we transitioned to strict mode.
     if ((kind == FunctionSyntaxKind::Statement || kind == FunctionSyntaxKind::Expression) &&
         fun->explicitName() &&
@@ -8581,16 +8607,20 @@ GeneralParser<ParseHandler, CharT>::unar
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       case TokenKind::Await: {
         if (pc->isAsync()) {
+            if (inParametersOfAsyncFunction()) {
+                error(JSMSG_AWAIT_IN_DEFAULT);
+                return null();
+            }
             Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
             if (!kid)
                 return null();
             pc->lastAwaitOffset = begin;
             return handler.newAwaitExpression(begin, kid);
         }
       }
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -244,16 +244,19 @@ enum class PropertyType {
     DerivedConstructor
 };
 
 enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
 
 template <class ParseHandler, typename CharT>
 class AutoAwaitIsKeyword;
 
+template <class ParseHandler, typename CharT>
+class AutoInParametersOfAsyncFunction;
+
 class MOZ_STACK_CLASS ParserBase
   : public StrictModeGetter,
     private JS::AutoGCRooter
 {
   private:
     ParserBase* thisForCtor() { return this; }
 
     // This is needed to cast a parser to JS::AutoGCRooter.
@@ -292,28 +295,35 @@ class MOZ_STACK_CLASS ParserBase
     bool checkOptionsCalled:1;
 #endif
 
     /* Unexpected end of input, i.e. Eof not at top-level. */
     bool isUnexpectedEOF_:1;
 
     /* AwaitHandling */ uint8_t awaitHandling_:2;
 
+    bool inParametersOfAsyncFunction_:1;
+
     /* ParseGoal */ uint8_t parseGoal_:1;
 
   public:
     bool awaitIsKeyword() const {
       return awaitHandling_ != AwaitIsName;
     }
 
+    bool inParametersOfAsyncFunction() const {
+        return inParametersOfAsyncFunction_;
+    }
+
     ParseGoal parseGoal() const {
         return ParseGoal(parseGoal_);
     }
 
     template<class, typename> friend class AutoAwaitIsKeyword;
+    template<class, typename> friend class AutoInParametersOfAsyncFunction;
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                bool foldConstants, UsedNameTracker& usedNames,
                ScriptSourceObject* sourceObject, ParseGoal parseGoal);
     ~ParserBase();
 
     bool checkOptions();
 
@@ -685,16 +695,17 @@ class MOZ_STACK_CLASS GeneralParser
     using Modifier = TokenStreamShared::Modifier;
     using Position = typename TokenStream::Position;
 
     using Base::PredictUninvoked;
     using Base::PredictInvoked;
 
     using Base::alloc;
     using Base::awaitIsKeyword;
+    using Base::inParametersOfAsyncFunction;
     using Base::parseGoal;
 #if DEBUG
     using Base::checkOptionsCalled;
 #endif
     using Base::finishFunctionScopes;
     using Base::finishLexicalScope;
     using Base::foldConstants;
     using Base::getFilename;
@@ -891,16 +902,17 @@ class MOZ_STACK_CLASS GeneralParser
     GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                   const CharT* chars, size_t length, bool foldConstants,
                   UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
                   LazyScript* lazyOuterFunction,
                   ScriptSourceObject* sourceObject,
                   ParseGoal parseGoal);
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
+    inline void setInParametersOfAsyncFunction(bool inParameters);
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
 
     /* Report the given error at the current offset. */
     void error(unsigned errorNumber, ...);
@@ -1350,16 +1362,17 @@ class MOZ_STACK_CLASS Parser<SyntaxParse
 
     PropertyName* bindingIdentifier(YieldHandling yieldHandling) {
         return Base::bindingIdentifier(yieldHandling);
     }
 
     // Functions present in both Parser<ParseHandler, CharT> specializations.
 
     inline void setAwaitHandling(AwaitHandling awaitHandling);
+    inline void setInParametersOfAsyncFunction(bool inParameters);
 
     Node newRegExp();
 
     // Parse a module.
     Node moduleBody(ModuleSharedContext* modulesc);
 
     inline Node importDeclaration();
     inline bool checkLocalExportNames(Node node);
@@ -1468,16 +1481,19 @@ class MOZ_STACK_CLASS Parser<FullParseHa
         return Base::bindingIdentifier(yieldHandling);
     }
 
     // Functions present in both Parser<ParseHandler, CharT> specializations.
 
     friend class AutoAwaitIsKeyword<SyntaxParseHandler, CharT>;
     inline void setAwaitHandling(AwaitHandling awaitHandling);
 
+    friend class AutoInParametersOfAsyncFunction<SyntaxParseHandler, CharT>;
+    inline void setInParametersOfAsyncFunction(bool inParameters);
+
     Node newRegExp();
 
     // Parse a module.
     Node moduleBody(ModuleSharedContext* modulesc);
 
     Node importDeclaration();
     bool checkLocalExportNames(Node node);
     bool checkExportedName(JSAtom* exportName);
@@ -1607,16 +1623,37 @@ class MOZ_STACK_CLASS AutoAwaitIsKeyword
             parser_->setAwaitHandling(awaitHandling);
     }
 
     ~AutoAwaitIsKeyword() {
         parser_->setAwaitHandling(oldAwaitHandling_);
     }
 };
 
+template <class ParseHandler, typename CharT>
+class MOZ_STACK_CLASS AutoInParametersOfAsyncFunction
+{
+    using GeneralParser = frontend::GeneralParser<ParseHandler, CharT>;
+
+  private:
+    GeneralParser* parser_;
+    bool oldInParametersOfAsyncFunction_;
+
+  public:
+    AutoInParametersOfAsyncFunction(GeneralParser* parser, bool inParameters) {
+        parser_ = parser;
+        oldInParametersOfAsyncFunction_ = parser_->inParametersOfAsyncFunction_;
+        parser_->setInParametersOfAsyncFunction(inParameters);
+    }
+
+    ~AutoInParametersOfAsyncFunction() {
+        parser_->setInParametersOfAsyncFunction(oldInParametersOfAsyncFunction_);
+    }
+};
+
 template <typename Scope>
 extern typename Scope::Data*
 NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings);
 
 mozilla::Maybe<GlobalScope::Data*>
 NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
 mozilla::Maybe<EvalScope::Data*>
 NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor: 
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1478910;
+var summary = 'JSMSG_AWAIT_IN_DEFAULT error for incomplete await expr in async function/generator parameter';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus(summary);
+
+  let testAwaitInDefaultExprOfAsyncFunc = (code) => {
+  	assertThrowsInstanceOf(() => eval(code), SyntaxError, "await can't be used in default expression");
+  };
+
+  let testNoException = (code) => {
+  	assertEq(completesNormally(code), true);
+  };
+
+  // https://www.ecma-international.org/ecma-262/9.0/
+
+  // Async Generator Function Definitions : AsyncGeneratorDeclaration & AsyncGeneratorExpression
+  // async function* f() {}
+  // f = async function*() {}
+  testAwaitInDefaultExprOfAsyncFunc("async function* f(a = await) {}");
+  testAwaitInDefaultExprOfAsyncFunc("let f = async function*(a = await) {}");
+
+  testAwaitInDefaultExprOfAsyncFunc("function f(a = async function*(a = await) {}) {}");
+  testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function*(a = await) {}; }");
+
+  testAwaitInDefaultExprOfAsyncFunc("async function* f() { a = async function*(a = await) {}; }");
+  testNoException("async function* f() { let a = function(a = await) {}; }");
+
+  testNoException("async function* f(a = async function*() { await 1; }) {}");
+
+  // Async Function Definitions : AsyncFunctionDeclaration & AsyncFunctionExpression
+  // async function f() {}
+  // f = async function() {}
+  testAwaitInDefaultExprOfAsyncFunc("async function f(a = await) {}");
+  testAwaitInDefaultExprOfAsyncFunc("let f = async function(a = await) {}");
+
+  testAwaitInDefaultExprOfAsyncFunc("function f(a = async function(a = await) {}) {}");
+  testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function(a = await) {}; }");
+
+  testAwaitInDefaultExprOfAsyncFunc("async function f() { a = async function(a = await) {}; }");
+  testNoException("async function f() { let a = function(a = await) {}; }");
+
+  testNoException("async function f(a = async function() { await 1; }) {}");
+
+  // Async Arrow Function Definitions : AsyncArrowFunction
+  // async () => {}
+  testAwaitInDefaultExprOfAsyncFunc("async (a = await) => {}");
+
+  testNoException("async (a = async () => { await 1; }) => {}");
+
+  reportCompare(true, true, summary);
+}