Bug 1356189 - Ensure 'await' is always a restricted identifier when parsing modules. r=shu
☠☠ backed out by 196bfa86b5a6 ☠ ☠
authorAndré Bargull <andre.bargull@gmail.com>
Tue, 06 Jun 2017 01:42:18 -0700
changeset 413203 47ce8dbb036e5452c0dd060c274cef056306d258
parent 413202 0048424c784a4a549874c10c8da15eb390653092
child 413204 f58a80807c734285f69680c036bc41f3cb74e6bd
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1356189
milestone55.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
@@ -796,17 +796,17 @@ ParserBase::ParserBase(JSContext* cx, Li
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
-    awaitIsKeyword_(false)
+    awaitHandling_(AwaitIsName)
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 ParserBase::~ParserBase()
 {
     alloc.release(tempPoolMark);
@@ -854,28 +854,28 @@ Parser<ParseHandler, CharT>::checkOption
 template <template <typename CharT> 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 (Parser<SyntaxParseHandler, char16_t>* parser = handler.getSyntaxParser())
-        parser->setAwaitIsKeyword(isKeyword);
+        parser->setAwaitHandling(awaitHandling);
 }
 
 ObjectBox*
 ParserBase::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
@@ -2233,17 +2233,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;
@@ -2501,16 +2501,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,
@@ -2561,17 +2569,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();
@@ -3671,18 +3680,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))
@@ -3743,20 +3754,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())
@@ -3909,17 +3921,17 @@ Parser<ParseHandler, CharT>::functionStm
 
 template <template <typename CharT> class ParseHandler, typename CharT>
 typename ParseHandler<CharT>::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 { 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.
@@ -810,21 +811,21 @@ class ParserBase : public StrictModeGett
      * is not known whether the parse succeeds or fails, this bit is set and
      * the parse will return false.
      */
     bool abortedSyntaxParse:1;
 
     /* Unexpected end of input, i.e. TOK_EOF not at top-level. */
     bool isUnexpectedEOF_:1;
 
-    bool awaitIsKeyword_:1;
+    AwaitHandling 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(); }
@@ -1130,17 +1131,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,
            Parser<SyntaxParseHandler, CharT>* syntaxParser, LazyScript* lazyOuterFunction);
     ~Parser();
 
     friend class AutoAwaitIsKeyword<Parser>;
-    void setAwaitIsKeyword(bool isKeyword);
+    void setAwaitHandling(AwaitHandling awaitHandling);
 
     bool checkOptions();
 
     friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
     /*
      * Parse a top-level JS script.
      */
@@ -1571,26 +1572,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_ = 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