Bug 1356189 - Ensure 'await' is always a restricted identifier when parsing modules. r=shu
authorAndré Bargull <andre.bargull@gmail.com>
Wed, 28 Jun 2017 12:22:19 -0700
changeset 415338 d365e0c892e30ea94209454ed26f57d1ad48c926
parent 415337 b7f85a213aaca5a17bf202800619b1075927c871
child 415339 8b1346172862351217927a431114e81465259a24
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1356189
milestone56.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 1356189 - Ensure 'await' is always a restricted identifier when parsing modules. r=shu
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/tests/ecma_6/Module/await-restricted-nested.js
js/src/tests/ecma_6/Module/browser.js
js/src/tests/ecma_6/Module/shell.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -841,17 +841,17 @@ ParserBase::ParserBase(JSContext* cx, Li
     usedNames(usedNames),
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     isUnexpectedEOF_(false),
-    awaitIsKeyword_(false)
+    awaitHandling_(AwaitIsName)
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 ParserBase::~ParserBase()
 {
     alloc.release(tempPoolMark);
@@ -900,28 +900,28 @@ Parser<ParseHandler, CharT>::checkOption
 template <class ParseHandler, typename CharT>
 Parser<ParseHandler, CharT>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
 template <>
 void
-Parser<SyntaxParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
-{
-    awaitIsKeyword_ = isKeyword;
+Parser<SyntaxParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    awaitHandling_ = awaitHandling;
 }
 
 template <>
 void
-Parser<FullParseHandler, char16_t>::setAwaitIsKeyword(bool isKeyword)
-{
-    awaitIsKeyword_ = isKeyword;
+Parser<FullParseHandler, char16_t>::setAwaitHandling(AwaitHandling awaitHandling)
+{
+    awaitHandling_ = awaitHandling;
     if (syntaxParser_)
-        syntaxParser_->setAwaitIsKeyword(isKeyword);
+        syntaxParser_->setAwaitHandling(awaitHandling);
 }
 
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
@@ -2279,17 +2279,17 @@ Parser<FullParseHandler, char16_t>::modu
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule(pos());
     if (!mn)
         return null();
 
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, true);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, AwaitIsModuleKeyword);
     ParseNode* pn = statementList(YieldIsKeyword);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2547,16 +2547,24 @@ Parser<SyntaxParseHandler, char16_t>::fi
 static YieldHandling
 GetYieldHandling(GeneratorKind generatorKind)
 {
     if (generatorKind == NotGenerator)
         return YieldIsName;
     return YieldIsKeyword;
 }
 
+static AwaitHandling
+GetAwaitHandling(FunctionAsyncKind asyncKind)
+{
+    if (asyncKind == SyncFunction)
+        return AwaitIsName;
+    return AwaitIsKeyword;
+}
+
 template <>
 ParseNode*
 Parser<FullParseHandler, char16_t>::standaloneFunction(HandleFunction fun,
                                                        HandleScope enclosingScope,
                                                        const Maybe<uint32_t>& parameterListEnd,
                                                        GeneratorKind generatorKind,
                                                        FunctionAsyncKind asyncKind,
                                                        Directives inheritedDirectives,
@@ -2607,17 +2615,18 @@ Parser<FullParseHandler, char16_t>::stan
     funbox->initStandaloneFunction(enclosingScope);
 
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind);
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncKind == AsyncFunction);
+    AwaitHandling awaitHandling = GetAwaitHandling(asyncKind);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -3717,18 +3726,20 @@ Parser<ParseHandler, CharT>::functionFor
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
     // See below for an explanation why arrow function parameters and arrow
     // function bodies are parsed with different yield/await settings.
     {
-        bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword());
-        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncOrArrowInAsync);
+        AwaitHandling awaitHandling = funbox->isAsync() || (kind == Arrow && awaitIsKeyword())
+                                      ? AwaitIsKeyword
+                                      : AwaitIsName;
+        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, awaitHandling);
         if (!functionArguments(yieldHandling, kind, pn))
             return false;
     }
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
@@ -3789,20 +3800,21 @@ Parser<ParseHandler, CharT>::functionFor
 
     // 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.
     // 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<Parser> awaitIsKeyword(this, funbox->isAsync());
+        AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, bodyAwaitHandling);
         body = functionBody(inHandling, bodyYieldHandling, kind, bodyType);
         if (!body)
             return false;
     }
 
     // Revalidate the function name when we transitioned to strict mode.
     if ((kind == Statement || kind == Expression) && fun->explicitName()
         && !inheritedStrict && pc->sc()->strict())
@@ -3955,17 +3967,17 @@ Parser<ParseHandler, CharT>::functionStm
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::functionExpr(uint32_t toStringStart, InvokedPrediction invoked,
                                           FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
-    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, asyncKind == AsyncFunction);
+    AutoAwaitIsKeyword<Parser> awaitIsKeyword(this, GetAwaitHandling(asyncKind));
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
         if (!asyncIterationSupported()) {
             if (asyncKind != SyncFunction) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -604,16 +604,17 @@ enum class PropertyType {
     DerivedConstructor
 };
 
 // Specify a value for an ES6 grammar parametrization.  We have no enum for
 // [Return] because its behavior is exactly equivalent to checking whether
 // we're in a function box -- easier and simpler than passing an extra
 // parameter everywhere.
 enum YieldHandling { YieldIsName, YieldIsKeyword };
+enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword };
 enum InHandling { InAllowed, InProhibited };
 enum DefaultHandling { NameRequired, AllowDefaultName };
 enum TripledotHandling { TripledotAllowed, TripledotProhibited };
 
 // A data structure for tracking used names per parsing session in order to
 // compute which bindings are closed over. Scripts and scopes are numbered
 // monotonically in textual order and name uses are tracked by lists of
 // (script id, scope id) pairs of their use sites.
@@ -803,21 +804,21 @@ class ParserBase : public StrictModeGett
 #if DEBUG
     /* Our fallible 'checkOptions' member function has been called. */
     bool checkOptionsCalled:1;
 #endif
 
     /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
     bool isUnexpectedEOF_:1;
 
-    bool awaitIsKeyword_:1;
+    /* AwaitHandling */ uint8_t awaitHandling_:2;
 
   public:
     bool awaitIsKeyword() const {
-      return awaitIsKeyword_;
+      return awaitHandling_ != AwaitIsName;
     }
 
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames, LazyScript* lazyOuterFunction);
     ~ParserBase();
 
     const char* getFilename() const { return tokenStream.getFilename(); }
@@ -1129,17 +1130,17 @@ class Parser final : public ParserBase, 
 
   public:
     Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
            const CharT* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
            SyntaxParser* syntaxParser, LazyScript* lazyOuterFunction);
     ~Parser();
 
     friend class AutoAwaitIsKeyword<Parser>;
-    void setAwaitIsKeyword(bool isKeyword);
+    void setAwaitHandling(AwaitHandling awaitHandling);
 
     // If ParseHandler is SyntaxParseHandler, whether the last syntax parse was
     // aborted due to unsupported language constructs.
     //
     // If ParseHandler is FullParseHandler, false.
     bool hadAbortedSyntaxParse();
 
     // If ParseHandler is SyntaxParseHandler, clear whether the last syntax
@@ -1594,26 +1595,30 @@ class Parser final : public ParserBase, 
     bool asmJS(Node list);
 };
 
 template <class Parser>
 class MOZ_STACK_CLASS AutoAwaitIsKeyword
 {
   private:
     Parser* parser_;
-    bool oldAwaitIsKeyword_;
+    AwaitHandling oldAwaitHandling_;
 
   public:
-    AutoAwaitIsKeyword(Parser* parser, bool awaitIsKeyword) {
+    AutoAwaitIsKeyword(Parser* parser, AwaitHandling awaitHandling) {
         parser_ = parser;
-        oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
-        parser_->setAwaitIsKeyword(awaitIsKeyword);
+        oldAwaitHandling_ = static_cast<AwaitHandling>(parser_->awaitHandling_);
+
+        // 'await' is always a keyword in module contexts, so we don't modify
+        // the state when the original handling is AwaitIsModuleKeyword.
+        if (oldAwaitHandling_ != AwaitIsModuleKeyword)
+            parser_->setAwaitHandling(awaitHandling);
     }
 
     ~AutoAwaitIsKeyword() {
-        parser_->setAwaitIsKeyword(oldAwaitIsKeyword_);
+        parser_->setAwaitHandling(oldAwaitHandling_);
     }
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_Parser_h */
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Module/await-restricted-nested.js
@@ -0,0 +1,6 @@
+// |reftest| error:SyntaxError module
+
+// 'await' is always a keyword when parsing modules.
+function f() {
+    await;
+}
new file mode 100644
new file mode 100644