Bug 1336783 - Part 1: Rework on reserved word and remove TokenStream::KeywordIsName. r=jwalden
authorTooru Fujisawa <arai_a@mac.com>
Wed, 15 Feb 2017 23:53:05 +0900
changeset 342994 476a938ee2abc89377b9451295735919c51259f9
parent 342993 b29e907e4fbbd6dd635902722f3eaab658685e36
child 342995 761227e357b95f345ec43df0b2be4cba88393ef4
push id87011
push userarai_a@mac.com
push dateWed, 15 Feb 2017 14:54:02 +0000
treeherdermozilla-inbound@aeca66c82a3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1336783
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 1336783 - Part 1: Rework on reserved word and remove TokenStream::KeywordIsName. r=jwalden
config/check_spidermonkey_style.py
js/src/frontend/GenerateReservedWords.py
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/ReservedWords.h
js/src/frontend/TokenKind.h
js/src/frontend/TokenStream.cpp
js/src/frontend/TokenStream.h
js/src/jit-test/tests/modules/export-declaration.js
js/src/js.msg
js/src/jsatom.cpp
js/src/jsatom.h
js/src/jsautokw.py
js/src/moz.build
js/src/vm/CommonPropertyNames.h
js/src/vm/Keywords.h
js/src/wasm/AsmJS.cpp
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -58,17 +58,17 @@ ignored_js_src_dirs = [
 
 # We ignore #includes of these files, because they don't follow the usual rules.
 included_inclnames_to_ignore = set([
     'ffi.h',                    # generated in ctypes/libffi/
     'devtools/sharkctl.h',      # we ignore devtools/ in general
     'devtools/Instruments.h',   # we ignore devtools/ in general
     'double-conversion.h',      # strange MFBT case
     'javascript-trace.h',       # generated in $OBJDIR if HAVE_DTRACE is defined
-    'jsautokw.h',               # generated in $OBJDIR
+    'frontend/ReservedWordsGenerated.h', # generated in $OBJDIR
     'jscustomallocator.h',      # provided by embedders;  allowed to be missing
     'js-config.h',              # generated in $OBJDIR
     'fdlibm.h',                 # fdlibm
     'pratom.h',                 # NSPR
     'prcvar.h',                 # NSPR
     'prerror.h',                # NSPR
     'prinit.h',                 # NSPR
     'prio.h',                   # NSPR
@@ -96,17 +96,17 @@ included_inclnames_to_ignore = set([
     'unicode/utypes.h',         # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
 
 # These files have additional constraints on where they are #included, so we
 # ignore #includes of them when checking #include ordering.
 oddly_ordered_inclnames = set([
     'ctypes/typedefs.h',        # Included multiple times in the body of ctypes/CTypes.h
-    'jsautokw.h',               # Included in the body of frontend/TokenStream.h
+    'frontend/ReservedWordsGenerated.h', # Included in the body of frontend/TokenStream.h
     'jswin.h',                  # Must be #included before <psapi.h>
     'machine/endian.h',         # Must be included after <sys/types.h> on BSD
     'winbase.h',                # Must precede other system headers(?)
     'windef.h'                  # Must precede other system headers(?)
 ])
 
 # The files in tests/style/ contain code that fails this checking in various
 # ways.  Here is the output we expect.  If the actual output differs from
rename from js/src/jsautokw.py
rename to js/src/frontend/GenerateReservedWords.py
--- a/js/src/jsautokw.py
+++ b/js/src/frontend/GenerateReservedWords.py
@@ -1,61 +1,61 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 import sys
 
-def read_keyword_list(filename):
+def read_reserved_word_list(filename):
     macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$")
 
-    keyword_list = []
+    reserved_word_list = []
     index = 0
     with open(filename, 'r') as f:
         for line in f:
             m = macro_pat.search(line)
             if m:
-                keyword_list.append((index, m.group(1)))
+                reserved_word_list.append((index, m.group(1)))
                 index += 1
 
-    assert(len(keyword_list) != 0)
+    assert(len(reserved_word_list) != 0)
 
-    return keyword_list
+    return reserved_word_list
 
 def line(opt, s):
     opt['output'].write('{}{}\n'.format('    ' * opt['indent_level'], s))
 
 def indent(opt):
     opt['indent_level'] += 1
 
 def dedent(opt):
     opt['indent_level'] -= 1
 
-def span_and_count_at(keyword_list, column):
-    assert(len(keyword_list) != 0);
+def span_and_count_at(reserved_word_list, column):
+    assert(len(reserved_word_list) != 0);
 
     chars_dict = {}
-    for index, keyword in keyword_list:
-        chars_dict[ord(keyword[column])] = True
+    for index, word in reserved_word_list:
+        chars_dict[ord(word[column])] = True
 
     chars = sorted(chars_dict.keys())
     return chars[-1] - chars[0] + 1, len(chars)
 
-def optimal_switch_column(opt, keyword_list, columns, unprocessed_columns):
-    assert(len(keyword_list) != 0);
+def optimal_switch_column(opt, reserved_word_list, columns, unprocessed_columns):
+    assert(len(reserved_word_list) != 0);
     assert(unprocessed_columns != 0);
 
     min_count = 0
     min_span = 0
     min_count_index = 0
     min_span_index = 0
 
     for index in range(0, unprocessed_columns):
-        span, count = span_and_count_at(keyword_list, columns[index])
+        span, count = span_and_count_at(reserved_word_list, columns[index])
         assert(span != 0)
 
         if span == 1:
             assert(count == 1)
             return 1, True
 
         assert(count != 1)
         if index == 0 or min_span > span:
@@ -66,148 +66,148 @@ def optimal_switch_column(opt, keyword_l
             min_count = count
             min_count_index = index
 
     if min_count <= opt['use_if_threshold']:
         return min_count_index, True
 
     return min_span_index, False
 
-def split_list_per_column(keyword_list, column):
-    assert(len(keyword_list) != 0);
+def split_list_per_column(reserved_word_list, column):
+    assert(len(reserved_word_list) != 0);
 
     column_dict = {}
-    for item in keyword_list:
-        index, keyword = item
-        per_column = column_dict.setdefault(keyword[column], [])
+    for item in reserved_word_list:
+        index, word = item
+        per_column = column_dict.setdefault(word[column], [])
         per_column.append(item)
 
-    return sorted(column_dict.items(), key=lambda (char, keyword): ord(char))
+    return sorted(column_dict.items(), key=lambda (char, word): ord(char))
 
-def generate_letter_switch(opt, unprocessed_columns, keyword_list,
+def generate_letter_switch(opt, unprocessed_columns, reserved_word_list,
                            columns=None):
-    assert(len(keyword_list) != 0);
+    assert(len(reserved_word_list) != 0);
 
     if not columns:
         columns = range(0, unprocessed_columns)
 
-    if len(keyword_list) == 1:
-        index, keyword = keyword_list[0]
+    if len(reserved_word_list) == 1:
+        index, word = reserved_word_list[0]
 
         if unprocessed_columns == 0:
-            line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+            line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
             return
 
         if unprocessed_columns > opt['char_tail_test_threshold']:
-            line(opt, 'JSKW_TEST_GUESS({}) /* {} */'.format(index, keyword))
+            line(opt, 'JSRW_TEST_GUESS({}) /* {} */'.format(index, word))
             return
 
         conds = []
         for column in columns[0:unprocessed_columns]:
-            quoted = repr(keyword[column])
-            conds.append('JSKW_AT({})=={}'.format(column, quoted))
+            quoted = repr(word[column])
+            conds.append('JSRW_AT({})=={}'.format(column, quoted))
 
         line(opt, 'if ({}) {{'.format(' && '.join(conds)))
 
         indent(opt)
-        line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+        line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
         dedent(opt)
 
         line(opt, '}')
-        line(opt, 'JSKW_NO_MATCH()')
+        line(opt, 'JSRW_NO_MATCH()')
         return
 
     assert(unprocessed_columns != 0);
 
-    optimal_column_index, use_if = optimal_switch_column(opt, keyword_list,
+    optimal_column_index, use_if = optimal_switch_column(opt, reserved_word_list,
                                                          columns,
                                                          unprocessed_columns)
     optimal_column = columns[optimal_column_index]
 
     # Make a copy to avoid breaking passed list.
     columns = columns[:]
     columns[optimal_column_index] = columns[unprocessed_columns - 1]
 
-    list_per_column = split_list_per_column(keyword_list, optimal_column)
+    list_per_column = split_list_per_column(reserved_word_list, optimal_column)
 
     if not use_if:
-        line(opt, 'switch (JSKW_AT({})) {{'.format(optimal_column))
+        line(opt, 'switch (JSRW_AT({})) {{'.format(optimal_column))
 
-    for char, keyword_list_per_column in list_per_column:
+    for char, reserved_word_list_per_column in list_per_column:
         quoted = repr(char)
         if use_if:
-            line(opt, 'if (JSKW_AT({}) == {}) {{'.format(optimal_column,
+            line(opt, 'if (JSRW_AT({}) == {}) {{'.format(optimal_column,
                                                          quoted))
         else:
             line(opt, '  case {}:'.format(quoted))
 
         indent(opt)
         generate_letter_switch(opt, unprocessed_columns - 1,
-                               keyword_list_per_column, columns)
+                               reserved_word_list_per_column, columns)
         dedent(opt)
 
         if use_if:
             line(opt, '}')
 
     if not use_if:
         line(opt, '}')
 
-    line(opt, 'JSKW_NO_MATCH()')
+    line(opt, 'JSRW_NO_MATCH()')
 
-def split_list_per_length(keyword_list):
-    assert(len(keyword_list) != 0);
+def split_list_per_length(reserved_word_list):
+    assert(len(reserved_word_list) != 0);
 
     length_dict = {}
-    for item in keyword_list:
-        index, keyword = item
-        per_length = length_dict.setdefault(len(keyword), [])
+    for item in reserved_word_list:
+        index, word = item
+        per_length = length_dict.setdefault(len(word), [])
         per_length.append(item)
 
-    return sorted(length_dict.items(), key=lambda (length, keyword): length)
+    return sorted(length_dict.items(), key=lambda (length, word): length)
 
-def generate_switch(opt, keyword_list):
-    assert(len(keyword_list) != 0);
+def generate_switch(opt, reserved_word_list):
+    assert(len(reserved_word_list) != 0);
 
     line(opt, '/*')
-    line(opt, ' * Generating switch for the list of {} entries:'.format(len(keyword_list)))
-    for index, keyword in keyword_list:
-        line(opt, ' * {}'.format(keyword))
+    line(opt, ' * Generating switch for the list of {} entries:'.format(len(reserved_word_list)))
+    for index, word in reserved_word_list:
+        line(opt, ' * {}'.format(word))
     line(opt, ' */')
 
-    list_per_length = split_list_per_length(keyword_list)
+    list_per_length = split_list_per_length(reserved_word_list)
 
     use_if = False
     if len(list_per_length) < opt['use_if_threshold']:
         use_if = True
 
     if not use_if:
-        line(opt, 'switch (JSKW_LENGTH()) {')
+        line(opt, 'switch (JSRW_LENGTH()) {')
 
-    for length, keyword_list_per_length in list_per_length:
+    for length, reserved_word_list_per_length in list_per_length:
         if use_if:
-            line(opt, 'if (JSKW_LENGTH() == {}) {{'.format(length))
+            line(opt, 'if (JSRW_LENGTH() == {}) {{'.format(length))
         else:
             line(opt, '  case {}:'.format(length))
 
         indent(opt)
-        generate_letter_switch(opt, length, keyword_list_per_length)
+        generate_letter_switch(opt, length, reserved_word_list_per_length)
         dedent(opt)
 
         if use_if:
             line(opt, '}')
 
     if not use_if:
         line(opt, '}')
-    line(opt, 'JSKW_NO_MATCH()')
+    line(opt, 'JSRW_NO_MATCH()')
 
-def main(output, keywords_h):
-    keyword_list = read_keyword_list(keywords_h)
+def main(output, reserved_words_h):
+    reserved_word_list = read_reserved_word_list(reserved_words_h)
 
     opt = {
         'indent_level': 1,
         'use_if_threshold': 3,
         'char_tail_test_threshold': 4,
         'output': output
     }
-    generate_switch(opt, keyword_list)
+    generate_switch(opt, reserved_word_list)
 
 if __name__ == '__main__':
     main(sys.stdout, *sys.argv[1:])
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -55,29 +55,37 @@ using JS::AutoGCRooter;
 namespace js {
 namespace frontend {
 
 using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr;
 using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr;
 using BindingIter = ParseContext::Scope::BindingIter;
 using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
 
-/* Read a token. Report an error and return null() if that token isn't of type tt. */
-#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber)                                     \
+// Read a token. Report an error and return null() if that token doesn't match
+// to the given func's condition.
+#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber)                              \
     JS_BEGIN_MACRO                                                                          \
         TokenKind token;                                                                    \
         if (!tokenStream.getToken(&token, modifier))                                        \
             return null();                                                                  \
-        if (token != tt) {                                                                  \
+        if (!(func)(token)) {                                                               \
             error(errorNumber);                                                             \
             return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 
-#define MUST_MATCH_TOKEN(tt, errno) MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errno)
+#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
+    MUST_MATCH_TOKEN_FUNC_MOD([](TokenKind tok) { return tok == tt; }, modifier, errorNumber)
+
+#define MUST_MATCH_TOKEN(tt, errorNumber) \
+    MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber)
+
+#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
+    MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber)
 
 template <class T, class U>
 static inline void
 PropagateTransitiveParseFlags(const T* inner, U* outer)
 {
     if (inner->bindingsAccessedDynamically())
         outer->setBindingsAccessedDynamically();
     if (inner->hasDebuggerStatement())
@@ -706,17 +714,18 @@ ParserBase::ParserBase(JSContext* cx, Li
     sct(nullptr),
     ss(nullptr),
     keepAtoms(cx),
     foldConstants(foldConstants),
 #ifdef DEBUG
     checkOptionsCalled(false),
 #endif
     abortedSyntaxParse(false),
-    isUnexpectedEOF_(false)
+    isUnexpectedEOF_(false),
+    awaitIsKeyword_(false)
 {
     cx->frontendCollectionPool().addActiveCompilation();
     tempPoolMark = alloc.mark();
 }
 
 ParserBase::~ParserBase()
 {
     alloc.release(tempPoolMark);
@@ -763,16 +772,32 @@ Parser<ParseHandler>::checkOptions()
 }
 
 template <typename ParseHandler>
 Parser<ParseHandler>::~Parser()
 {
     MOZ_ASSERT(checkOptionsCalled);
 }
 
+template <>
+void
+Parser<SyntaxParseHandler>::setAwaitIsKeyword(bool isKeyword)
+{
+    awaitIsKeyword_ = isKeyword;
+}
+
+template <>
+void
+Parser<FullParseHandler>::setAwaitIsKeyword(bool isKeyword)
+{
+    awaitIsKeyword_ = isKeyword;
+    if (Parser<SyntaxParseHandler>* parser = handler.syntaxParser)
+        parser->setAwaitIsKeyword(isKeyword);
+}
+
 template <typename ParseHandler>
 ObjectBox*
 Parser<ParseHandler>::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
@@ -887,45 +912,63 @@ Parser<ParseHandler>::parse()
             return null();
     }
 
     return pn;
 }
 
 /*
  * Strict mode forbids introducing new definitions for 'eval', 'arguments', or
- * for any strict mode reserved keyword.
+ * for any strict mode reserved word.
  */
 bool
 ParserBase::isValidStrictBinding(PropertyName* name)
 {
     return name != context->names().eval &&
            name != context->names().arguments &&
            name != context->names().let &&
            name != context->names().static_ &&
-           !(IsKeyword(name) && name != context->names().await);
+           name != context->names().yield &&
+           !IsStrictReservedWord(name);
 }
 
 /*
  * Check that it is permitted to introduce a binding for |name|. Use |pos| for
  * reporting error locations.
  */
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
 {
     if (!pc->sc()->needStrictChecks())
         return true;
 
-    if (!isValidStrictBinding(name)) {
-        JSAutoByteString bytes;
-        if (!AtomToPrintableString(context, name, &bytes))
-            return false;
-        return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, bytes.ptr());
-    }
+    if (name == context->names().arguments)
+        return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "arguments");
+
+    if (name == context->names().eval)
+        return strictModeErrorAt(pos.begin, JSMSG_BAD_BINDING, "eval");
+
+    if (name == context->names().let) {
+        errorAt(pos.begin, JSMSG_RESERVED_ID, "let");
+        return false;
+    }
+
+    if (name == context->names().static_) {
+        errorAt(pos.begin, JSMSG_RESERVED_ID, "static");
+        return false;
+    }
+
+    if (name == context->names().yield) {
+        errorAt(pos.begin, JSMSG_RESERVED_ID, "yield");
+        return false;
+    }
+
+    if (IsStrictReservedWord(name))
+        return strictModeErrorAt(pos.begin, JSMSG_RESERVED_ID, ReservedWordToCharZ(name));
 
     return true;
 }
 
 /*
  * Returns true if all parameter names are valid strict mode binding names and
  * no duplicate parameter names are present.
  */
@@ -2038,17 +2081,17 @@ Parser<FullParseHandler>::moduleBody(Mod
     ParseContext::VarScope varScope(this);
     if (!varScope.init(pc))
         return nullptr;
 
     Node mn = handler.newModule();
     if (!mn)
         return null();
 
-    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
+    AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, true);
     ParseNode* pn = statementList(YieldIsKeyword);
     if (!pn)
         return null();
 
     MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     mn->pn_body = pn;
 
     TokenKind tt;
@@ -2334,17 +2377,17 @@ Parser<FullParseHandler>::standaloneFunc
     funbox->initStandaloneFunction(enclosingScope);
 
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
-    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+    AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
                                          parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
@@ -2637,20 +2680,17 @@ Parser<ParseHandler>::matchOrInsertSemic
          *   await f();
          *        ^
          *        |
          *        tried to insert semicolon here
          *
          * Detect this situation and throw an understandable error.  Otherwise
          * we'd throw a confusing "missing ; before statement" error.
          */
-        if (!pc->isAsync() &&
-            tokenStream.currentToken().type == TOK_NAME &&
-            tokenStream.currentName() == context->names().await)
-        {
+        if (!pc->isAsync() && tokenStream.currentToken().type == TOK_AWAIT) {
             error(JSMSG_AWAIT_OUTSIDE_ASYNC);
             return false;
         }
 
         /* Advance the scanner for proper error location reporting. */
         tokenStream.consumeKnownToken(tt, modifier);
         error(JSMSG_SEMI_BEFORE_STMNT);
         return false;
@@ -2763,17 +2803,17 @@ Parser<ParseHandler>::functionArguments(
         TokenKind tt;
         // In async function, the first token after `async` is already gotten
         // with TokenStream::None.
         // In sync function, the first token is already gotten with
         // TokenStream::Operand.
         firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
         if (!tokenStream.peekToken(&tt, firstTokenModifier))
             return false;
-        if (tt == TOK_NAME || tt == TOK_YIELD) {
+        if (TokenKindIsPossibleIdentifier(tt)) {
             parenFreeArrow = true;
             argModifier = firstTokenModifier;
         }
     }
     if (!parenFreeArrow) {
         TokenKind tt;
         if (!tokenStream.getToken(&tt, firstTokenModifier))
             return false;
@@ -2819,17 +2859,17 @@ Parser<ParseHandler>::functionArguments(
                 error(JSMSG_PARAMETER_AFTER_REST);
                 return false;
             }
 
             TokenKind tt;
             if (!tokenStream.getToken(&tt, argModifier))
                 return false;
             argModifier = TokenStream::Operand;
-            MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
+            MOZ_ASSERT_IF(parenFreeArrow, TokenKindIsPossibleIdentifier(tt));
 
             if (tt == TOK_TRIPLEDOT) {
                 if (IsSetterKind(kind)) {
                     error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 disallowDuplicateParams = true;
@@ -2840,17 +2880,17 @@ Parser<ParseHandler>::functionArguments(
                 }
 
                 hasRest = true;
                 funbox->setHasRest();
 
                 if (!tokenStream.getToken(&tt))
                     return false;
 
-                if (tt != TOK_NAME && tt != TOK_YIELD && tt != TOK_LB && tt != TOK_LC) {
+                if (!TokenKindIsPossibleIdentifier(tt) && tt != TOK_LB && tt != TOK_LC) {
                     error(JSMSG_NO_REST_NAME);
                     return false;
                 }
             }
 
             switch (tt) {
               case TOK_LB:
               case TOK_LC: {
@@ -2870,48 +2910,39 @@ Parser<ParseHandler>::functionArguments(
                     return false;
 
                 if (!noteDestructuredPositionalFormalParameter(funcpn, destruct))
                     return false;
 
                 break;
               }
 
-              case TOK_NAME:
-              case TOK_YIELD: {
+              default: {
+                if (!TokenKindIsPossibleIdentifier(tt)) {
+                    error(JSMSG_MISSING_FORMAL);
+                    return false;
+                }
+
                 if (parenFreeArrow)
                     funbox->setStart(tokenStream);
 
-                if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
-                    // `await` is already gotten as TOK_NAME for the following
-                    // case:
-                    //
-                    //   async await => 1
-                    error(JSMSG_RESERVED_ID, "await");
-                    return false;
-                }
-
                 RootedPropertyName name(context, bindingIdentifier(yieldHandling));
                 if (!name)
                     return false;
 
                 if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
                                                    &duplicatedParam))
                 {
                     return false;
                 }
                 if (duplicatedParam)
                     funbox->hasDuplicateParameters = true;
 
                 break;
               }
-
-              default:
-                error(JSMSG_MISSING_FORMAL);
-                return false;
             }
 
             if (positionalFormals.length() >= ARGNO_LIMIT) {
                 error(JSMSG_TOO_MANY_FUN_ARGS);
                 return false;
             }
 
             bool matched;
@@ -3419,17 +3450,17 @@ Parser<ParseHandler>::functionFormalPara
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
-    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, funbox->isAsync());
+    AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
     if (!functionArguments(yieldHandling, kind, pn))
         return false;
 
     Maybe<ParseContext::VarScope> varScope;
     if (funbox->hasParameterExprs) {
         varScope.emplace(this);
         if (!varScope->init(pc))
             return false;
@@ -3569,17 +3600,17 @@ Parser<ParseHandler>::functionStmt(Yield
             return null();
         }
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     RootedPropertyName name(context);
-    if (tt == TOK_NAME || tt == TOK_YIELD) {
+    if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (defaultHandling == AllowDefaultName) {
         name = context->names().starDefaultStar;
         tokenStream.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
@@ -3624,17 +3655,17 @@ Parser<ParseHandler>::functionStmt(Yield
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionExpr(InvokedPrediction invoked, FunctionAsyncKind asyncKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
-    AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
+    AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
     GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_MUL) {
         if (asyncKind != SyncFunction) {
             error(JSMSG_ASYNC_GENERATOR);
@@ -3643,17 +3674,17 @@ Parser<ParseHandler>::functionExpr(Invok
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
 
     RootedPropertyName name(context);
-    if (tt == TOK_NAME || tt == TOK_YIELD) {
+    if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else {
         tokenStream.ungetToken();
     }
 
     Node pn = handler.newFunctionExpression();
@@ -3680,28 +3711,16 @@ IsEscapeFreeStringLiteral(const TokenPos
     /*
      * If the string's length in the source code is its length as a value,
      * accounting for the quotes, then it must not contain any escape
      * sequences or line continuations.
      */
     return pos.begin + str->length() + 2 == pos.end;
 }
 
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkUnescapedName()
-{
-    const Token& token = tokenStream.currentToken();
-    if (!token.nameContainsEscape())
-        return true;
-
-    errorAt(token.pos.begin, JSMSG_ESCAPED_KEYWORD);
-    return false;
-}
-
 template <>
 bool
 Parser<SyntaxParseHandler>::asmJS(Node list)
 {
     // While asm.js could technically be validated and compiled during syntax
     // parsing, we have no guarantee that some later JS wouldn't abort the
     // syntax parse and cause us to re-parse (and re-compile) the asm.js module.
     // For simplicity, unconditionally abort the syntax parse when "use asm" is
@@ -3910,17 +3929,17 @@ Parser<ParseHandler>::condition(InHandli
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
-    if (tt == TOK_NAME || tt == TOK_YIELD) {
+    if (TokenKindIsPossibleIdentifier(tt)) {
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
 
         label.set(labelIdentifier(yieldHandling));
         if (!label)
             return false;
     } else {
         label.set(nullptr);
     }
@@ -4463,18 +4482,18 @@ Parser<ParseHandler>::initializerInNameD
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, TokenKind tt,
                                       bool initialDeclaration, YieldHandling yieldHandling,
                                       ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
 {
-    // Anything other than TOK_YIELD or TOK_NAME is an error.
-    if (tt != TOK_NAME && tt != TOK_YIELD) {
+    // Anything other than possible identifier is an error.
+    if (!TokenKindIsPossibleIdentifier(tt)) {
         error(JSMSG_NO_VARIABLE_NAME);
         return null();
     }
 
     RootedPropertyName name(context, bindingIdentifier(yieldHandling));
     if (!name)
         return null();
 
@@ -4634,63 +4653,50 @@ template <>
 bool
 Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet)
 {
     if (tt == TOK_LC) {
         while (true) {
             // Handle the forms |import {} from 'a'| and
             // |import { ..., } from 'a'| (where ... is non empty), by
             // escaping the loop early if the next token is }.
-            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+            if (!tokenStream.getToken(&tt))
                 return false;
 
             if (tt == TOK_RC)
                 break;
 
-            // If the next token is a keyword, the previous call to
-            // peekToken matched it as a TOK_NAME, and put it in the
-            // lookahead buffer, so this call will match keywords as well.
-            if (tt != TOK_NAME) {
+            if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_IMPORT_NAME);
                 return false;
             }
 
             Rooted<PropertyName*> importName(context, tokenStream.currentName());
             TokenPos importNamePos = pos();
 
-            TokenKind maybeAs;
-            if (!tokenStream.peekToken(&maybeAs))
-                return null();
-
-            if (maybeAs == TOK_NAME &&
-                tokenStream.nextName() == context->names().as)
-            {
-                tokenStream.consumeKnownToken(TOK_NAME);
-
-                if (!checkUnescapedName())
-                    return false;
-
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_AS))
+                return null();
+
+            if (matched) {
                 TokenKind afterAs;
                 if (!tokenStream.getToken(&afterAs))
                     return false;
 
-                if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
+                if (!TokenKindIsPossibleIdentifierName(afterAs)) {
                     error(JSMSG_NO_BINDING_NAME);
                     return false;
                 }
             } else {
                 // Keywords cannot be bound to themselves, so an import name
                 // that is a keyword is a syntax error if it is not followed
                 // by the keyword 'as'.
                 // See the ImportSpecifier production in ES6 section 15.2.2.
                 if (IsKeyword(importName)) {
-                    JSAutoByteString bytes;
-                    if (!AtomToPrintableString(context, importName, &bytes))
-                        return false;
-                    error(JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
+                    error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName));
                     return false;
                 }
             }
 
             RootedPropertyName bindingAtom(context, importedBinding());
             if (!bindingAtom)
                 return false;
 
@@ -4719,28 +4725,20 @@ Parser<FullParseHandler>::namedImportsOr
 
             if (next != TOK_COMMA) {
                 error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
                 return false;
             }
         }
     } else {
         MOZ_ASSERT(tt == TOK_MUL);
-        if (!tokenStream.getToken(&tt))
-            return false;
-
-        if (tt != TOK_NAME || tokenStream.currentName() != context->names().as) {
-            error(JSMSG_AS_AFTER_IMPORT_STAR);
-            return false;
-        }
-
-        if (!checkUnescapedName())
-            return false;
-
-        MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+
+        MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
+
+        MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME);
 
         Node importName = newName(context->names().star);
         if (!importName)
             return false;
 
         // Namespace imports are are not indirect bindings but lexical
         // definitions that hold a module namespace object. They are treated
         // as const variables which are initialized during the
@@ -4791,18 +4789,25 @@ Parser<FullParseHandler>::importDeclarat
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
     if (!importSpecSet)
         return null();
 
-    if (tt == TOK_NAME || tt == TOK_LC || tt == TOK_MUL) {
-        if (tt == TOK_NAME) {
+    if (tt == TOK_STRING) {
+        // Handle the form |import 'a'| by leaving the list empty. This is
+        // equivalent to |import {} from 'a'|.
+        importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
+    } else {
+        if (tt == TOK_LC || tt == TOK_MUL) {
+            if (!namedImportsOrNamespaceImport(tt, importSpecSet))
+                return null();
+        } else if (TokenKindIsPossibleIdentifierName(tt)) {
             // Handle the form |import a from 'b'|, by adding a single import
             // specifier to the list, with 'default' as the import name and
             // 'a' as the binding name. This is equivalent to
             // |import { default as a } from 'b'|.
             Node importName = newName(context->names().default_);
             if (!importName)
                 return null();
 
@@ -4835,39 +4840,29 @@ Parser<FullParseHandler>::importDeclarat
                     error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT);
                     return null();
                 }
 
                 if (!namedImportsOrNamespaceImport(tt, importSpecSet))
                     return null();
             }
         } else {
-            if (!namedImportsOrNamespaceImport(tt, importSpecSet))
-                return null();
+            error(JSMSG_DECLARATION_AFTER_IMPORT);
+            return null();
         }
 
         if (!tokenStream.getToken(&tt))
             return null();
 
-        if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
+        if (tt != TOK_FROM) {
             error(JSMSG_FROM_AFTER_IMPORT_CLAUSE);
             return null();
         }
 
-        if (!checkUnescapedName())
-            return null();
-
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
-    } else if (tt == TOK_STRING) {
-        // Handle the form |import 'a'| by leaving the list empty. This is
-        // equivalent to |import {} from 'a'|.
-        importSpecSet->pn_pos.end = importSpecSet->pn_pos.begin;
-    } else {
-        error(JSMSG_DECLARATION_AFTER_IMPORT);
-        return null();
     }
 
     Node moduleSpec = stringLiteral();
     if (!moduleSpec)
         return null();
 
     if (!matchOrInsertSemicolonAfterNonExpression())
         return null();
@@ -4963,30 +4958,30 @@ Parser<FullParseHandler>::exportDeclarat
             // is non empty), by escaping the loop early if the next token
             // is }.
             if (!tokenStream.getToken(&tt))
                 return null();
 
             if (tt == TOK_RC)
                 break;
 
-            if (tt != TOK_NAME) {
+            if (!TokenKindIsPossibleIdentifierName(tt)) {
                 error(JSMSG_NO_BINDING_NAME);
                 return null();
             }
 
             Node bindingName = newName(tokenStream.currentName());
             if (!bindingName)
                 return null();
 
             bool foundAs;
-            if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
+            if (!tokenStream.matchToken(&foundAs, TOK_AS))
                 return null();
             if (foundAs)
-                MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_EXPORT_NAME);
+                MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_EXPORT_NAME);
 
             Node exportName = newName(tokenStream.currentName());
             if (!exportName)
                 return null();
 
             if (!checkExportedName(exportName->pn_atom))
                 return null();
 
@@ -5011,49 +5006,44 @@ Parser<FullParseHandler>::exportDeclarat
 
         // Careful!  If |from| follows, even on a new line, it must start a
         // FromClause:
         //
         //   export { x }
         //   from "foo"; // a single ExportDeclaration
         //
         // But if it doesn't, we might have an ASI opportunity in Operand
-        // context, so simply matching a contextual keyword won't work:
+        // context:
         //
         //   export { x }   // ExportDeclaration, terminated by ASI
         //   fro\u006D      // ExpressionStatement, the name "from"
         //
         // In that case let matchOrInsertSemicolonAfterNonExpression sort out
         // ASI or any necessary error.
-        TokenKind tt;
-        if (!tokenStream.getToken(&tt, TokenStream::Operand))
-            return null();
-
-        if (tt == TOK_NAME &&
-            tokenStream.currentToken().name() == context->names().from &&
-            !tokenStream.currentToken().nameContainsEscape())
-        {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
+            return null();
+
+        if (matched) {
             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
             Node moduleSpec = stringLiteral();
             if (!moduleSpec)
                 return null();
 
             if (!matchOrInsertSemicolonAfterNonExpression())
                 return null();
 
             ParseNode* node = handler.newExportFromDeclaration(begin, kid, moduleSpec);
             if (!node || !pc->sc()->asModuleContext()->builder.processExportFrom(node))
                 return null();
 
             return node;
         }
 
-        tokenStream.ungetToken();
-
         if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
         break;
       }
 
       case TOK_MUL: {
         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
         if (!kid)
@@ -5064,24 +5054,21 @@ Parser<FullParseHandler>::exportDeclarat
         Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
         if (!exportSpec)
             return null();
 
         handler.addList(kid, exportSpec);
 
         if (!tokenStream.getToken(&tt))
             return null();
-        if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
+        if (tt != TOK_FROM) {
             error(JSMSG_FROM_AFTER_EXPORT_STAR);
             return null();
         }
 
-        if (!checkUnescapedName())
-            return null();
-
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
         Node moduleSpec = stringLiteral();
         if (!moduleSpec)
             return null();
 
         if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
@@ -5140,20 +5127,17 @@ Parser<FullParseHandler>::exportDeclarat
                 return null();
             break;
           case TOK_CLASS:
             kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
             if (!kid)
                 return null();
             break;
           default: {
-            if (tt == TOK_NAME &&
-                tokenStream.currentName() == context->names().async &&
-                !tokenStream.currentToken().nameContainsEscape())
-            {
+            if (tt == TOK_ASYNC) {
                 TokenKind nextSameLine = TOK_EOF;
                 if (!tokenStream.peekTokenSameLine(&nextSameLine))
                     return null();
 
                 if (nextSameLine == TOK_FUNCTION) {
                     tokenStream.consumeKnownToken(nextSameLine);
                     kid = functionStmt(YieldIsName, AllowDefaultName, AsyncFunction);
                     if (!kid)
@@ -5189,29 +5173,23 @@ Parser<FullParseHandler>::exportDeclarat
       case TOK_CONST:
         kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
         if (!kid)
             return null();
         if (!checkExportedNamesForDeclaration(kid))
             return null();
         break;
 
-      case TOK_NAME:
-        if (tokenStream.currentName() == context->names().let) {
-            if (!checkUnescapedName())
-                return null();
-
-            kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
-            if (!kid)
-                return null();
-            if (!checkExportedNamesForDeclaration(kid))
-                return null();
-            break;
-        }
-        MOZ_FALLTHROUGH;
+      case TOK_LET:
+        kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
+        if (!kid)
+            return null();
+        if (!checkExportedNamesForDeclaration(kid))
+            return null();
+        break;
 
       default:
         error(JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 
     ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
@@ -5251,20 +5229,17 @@ Parser<ParseHandler>::consequentOrAltern
         return null();
 
     // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in
     // non-strict code act as if they were braced: |if (x) function f() {}|
     // parses as |if (x) { function f() {} }|.
     //
     // Careful!  FunctionDeclaration doesn't include generators or async
     // functions.
-    if (next == TOK_NAME &&
-        !tokenStream.nextNameContainsEscape() &&
-        tokenStream.nextName() == context->names().async)
-    {
+    if (next == TOK_ASYNC) {
         tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
         // Peek only on the same line: ExpressionStatement's lookahead
         // restriction is phrased as
         //
         //   [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]
         //
         // meaning that code like this is valid:
@@ -5427,23 +5402,19 @@ template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
 {
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return false;
 
     *isForInp = tt == TOK_IN;
-    *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
-    if (!*isForInp && !*isForOfp) {
+    *isForOfp = tt == TOK_OF;
+    if (!*isForInp && !*isForOfp)
         tokenStream.ungetToken();
-    } else {
-        if (tt == TOK_NAME && !checkUnescapedName())
-            return false;
-    }
 
     MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp);
     return true;
 }
 
 template <class ParseHandler>
 bool
 Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
@@ -5483,25 +5454,22 @@ Parser<ParseHandler>::forHeadStart(Yield
     // For-in loop backwards compatibility requires that |let| starting a
     // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
     // parse as an identifier.  (|let| in for-of is always a declaration.)
     bool parsingLexicalDeclaration = false;
     bool letIsIdentifier = false;
     if (tt == TOK_CONST) {
         parsingLexicalDeclaration = true;
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
-    } else if (tt == TOK_NAME &&
-               tokenStream.nextName() == context->names().let &&
-               !tokenStream.nextNameContainsEscape())
-    {
+    } else if (tt == TOK_LET) {
         // We could have a {For,Lexical}Declaration, or we could have a
         // LeftHandSideExpression with lookahead restrictions so it's not
         // ambiguous with the former.  Check for a continuation of the former
         // to decide which we have.
-        tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+        tokenStream.consumeKnownToken(TOK_LET, TokenStream::Operand);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return false;
 
         parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next, yieldHandling);
         if (!parsingLexicalDeclaration) {
             tokenStream.ungetToken();
@@ -5612,17 +5580,17 @@ Parser<ParseHandler>::forStatement(Yield
 
     ParseContext::Statement stmt(pc, StatementKind::ForLoop);
 
     bool isForEach = false;
     unsigned iflags = 0;
 
     if (allowsForEachIn()) {
         bool matched;
-        if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+        if (!tokenStream.matchToken(&matched, TOK_EACH))
             return null();
         if (matched) {
             iflags = JSITER_FOREACH;
             isForEach = true;
             addTelemetry(JSCompartment::DeprecatedForEach);
             if (!warnOnceAboutForEach())
                 return null();
         }
@@ -6325,22 +6293,22 @@ Parser<ParseHandler>::tryStatement(Yield
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
      * kid3 is the finally statement
      *
      * catch nodes are ternary.
-     * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
+     * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC)
      * kid2 is the catch guard or null if no guard
      * kid3 is the catch block
      *
      * catch lvalue nodes are either:
-     *   TOK_NAME for a single identifier
+     *   a single identifier
      *   TOK_RB or TOK_RC for a destructuring left-hand side
      *
      * finally nodes are TOK_LC statement lists.
      */
 
     Node innerBlock;
     {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
@@ -6405,32 +6373,32 @@ Parser<ParseHandler>::tryStatement(Yield
               case TOK_LB:
               case TOK_LC:
                 catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
                                                      yieldHandling, tt);
                 if (!catchName)
                     return null();
                 break;
 
-              case TOK_NAME:
-              case TOK_YIELD: {
+              default: {
+                if (!TokenKindIsPossibleIdentifierName(tt)) {
+                    error(JSMSG_CATCH_IDENTIFIER);
+                    return null();
+                }
+
                 RootedPropertyName param(context, bindingIdentifier(yieldHandling));
                 if (!param)
                     return null();
                 catchName = newName(param);
                 if (!catchName)
                     return null();
                 if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos()))
                     return null();
                 break;
               }
-
-              default:
-                error(JSMSG_CATCH_IDENTIFIER);
-                return null();
             }
 
             Node catchGuard = null();
 #if JS_HAS_CATCH_GUARD
             /*
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
@@ -6579,17 +6547,17 @@ Parser<ParseHandler>::classDefinition(Yi
 
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     RootedPropertyName name(context);
-    if (tt == TOK_NAME || tt == TOK_YIELD) {
+    if (TokenKindIsPossibleIdentifier(tt)) {
         name = bindingIdentifier(yieldHandling);
         if (!name)
             return null();
     } else if (classContext == ClassStatement) {
         if (defaultHandling == AllowDefaultName) {
             name = context->names().starDefaultStar;
             tokenStream.ungetToken();
         } else {
@@ -6636,49 +6604,44 @@ Parser<ParseHandler>::classDefinition(Yi
 
     Node classMethods = handler.newClassMethodList(pos().begin);
     if (!classMethods)
         return null();
 
     bool seenConstructor = false;
     for (;;) {
         TokenKind tt;
-        if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+        if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
         if (tt == TOK_SEMI)
             continue;
 
         bool isStatic = false;
-        if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
-            if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
+        if (tt == TOK_STATIC) {
+            if (!tokenStream.peekToken(&tt))
                 return null();
             if (tt == TOK_RC) {
-                tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
+                tokenStream.consumeKnownToken(tt);
                 error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt));
                 return null();
             }
 
-            if (tt != TOK_LP) {
-                if (!checkUnescapedName())
-                    return null();
-
+            if (tt != TOK_LP)
                 isStatic = true;
-            } else {
-                tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+            else
                 tokenStream.ungetToken();
-            }
         } else {
             tokenStream.ungetToken();
         }
 
         uint32_t nameOffset;
-        if (!tokenStream.peekOffset(&nameOffset, TokenStream::KeywordIsName))
+        if (!tokenStream.peekOffset(&nameOffset))
             return null();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, classMethods, &propType, &propAtom);
         if (!propName)
             return null();
 
         if (propType != PropertyType::Getter && propType != PropertyType::Setter &&
@@ -6780,64 +6743,53 @@ Parser<ParseHandler>::classDefinition(Yi
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock);
 }
 
 template <class ParseHandler>
 bool
 Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
-    MOZ_ASSERT(tokenStream.currentName() == context->names().let);
-    MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
 
     // Destructuring is (for once) the easy case.
     if (next == TOK_LB || next == TOK_LC)
         return true;
 
+    // If we have the name "yield", the grammar parameter exactly states
+    // whether this is okay.  (This wasn't true for SpiderMonkey's ancient
+    // legacy generator syntax, but that's dead now.)  If YieldIsName,
+    // declaration-parsing code will (if necessary) enforce a strict mode
+    // restriction on defining "yield".  If YieldIsKeyword, consider this the
+    // end of the declaration, in case ASI induces a semicolon that makes the
+    // "yield" valid.
+    if (next == TOK_YIELD)
+        return yieldHandling == YieldIsName;
+
     // Otherwise a let declaration must have a name.
-    if (next == TOK_NAME) {
-        if (tokenStream.nextName() == context->names().yield) {
-            MOZ_ASSERT(tokenStream.nextNameContainsEscape(),
-                       "token stream should interpret unescaped 'yield' as TOK_YIELD");
-
-            // Same as |next == TOK_YIELD|.
-            return yieldHandling == YieldIsName;
-        }
-
-        // One non-"yield" TOK_NAME edge case deserves special comment.
-        // Consider this:
+    if (TokenKindIsPossibleIdentifier(next)) {
+        // A "let" edge case deserves special comment.  Consider this:
         //
         //   let     // not an ASI opportunity
         //   let;
         //
         // Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds
         // "let" into an early error.  Does this retroactively permit ASI so
         // that we should parse this as two ExpressionStatements?   No.  ASI
         // resolves during parsing.  Static semantics only apply to the full
         // parse tree with ASI applied.  No backsies!
         return true;
     }
 
-    // If we have the name "yield", the grammar parameter exactly states
-    // whether this is okay.  (This wasn't true for SpiderMonkey's ancient
-    // legacy generator syntax, but that's dead now.)  If YieldIsName,
-    // declaration-parsing code will (if necessary) enforce a strict mode
-    // restriction on defining "yield".  If YieldIsKeyword, consider this the
-    // end of the declaration, in case ASI induces a semicolon that makes the
-    // "yield" valid.
-    if (next == TOK_YIELD)
-        return yieldHandling == YieldIsName;
-
     // Otherwise not a let declaration.
     return false;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
 {
@@ -6890,47 +6842,52 @@ Parser<ParseHandler>::statement(YieldHan
             return null();
 
         if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
-      case TOK_NAME: {
+      default: {
+        // Avoid getting next token with None.
+        if (tt == TOK_AWAIT && pc->isAsync())
+            return expressionStatement(yieldHandling);
+
+        if (!TokenKindIsPossibleIdentifier(tt))
+            return expressionStatement(yieldHandling);
+
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
         // |let| here can only be an Identifier, not a declaration.  Give nicer
         // errors for declaration-looking typos.
-        if (!tokenStream.currentToken().nameContainsEscape() &&
-            tokenStream.currentName() == context->names().let)
-        {
+        if (tt == TOK_LET) {
             bool forbiddenLetDeclaration = false;
 
             if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
                 // |let| can't be an Identifier in strict mode code.  Ditto for
                 // non-standard JavaScript 1.7+.
                 forbiddenLetDeclaration = true;
             } else if (next == TOK_LB) {
                 // Enforce ExpressionStatement's 'let [' lookahead restriction.
                 forbiddenLetDeclaration = true;
-            } else if (next == TOK_LC || next == TOK_NAME) {
+            } else if (next == TOK_LC || TokenKindIsPossibleIdentifier(next)) {
                 // 'let {' and 'let foo' aren't completely forbidden, if ASI
                 // causes 'let' to be the entire Statement.  But if they're
                 // same-line, we can aggressively give a better error message.
                 //
                 // Note that this ignores 'yield' as TOK_YIELD: we'll handle it
                 // correctly but with a worse error message.
                 TokenKind nextSameLine;
                 if (!tokenStream.peekTokenSameLine(&nextSameLine))
                     return null();
 
-                MOZ_ASSERT(nextSameLine == TOK_NAME ||
+                MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) ||
                            nextSameLine == TOK_LC ||
                            nextSameLine == TOK_EOL);
 
                 forbiddenLetDeclaration = nextSameLine != TOK_EOL;
             }
 
             if (forbiddenLetDeclaration) {
                 error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations");
@@ -6944,19 +6901,16 @@ Parser<ParseHandler>::statement(YieldHan
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
 
-      default:
-        return expressionStatement(yieldHandling);
-
       // IfStatement[?Yield, ?Return]
       case TOK_IF:
         return ifStatement(yieldHandling);
 
       // BreakableStatement[?Yield, ?Return]
       //
       // BreakableStatement[Yield, Return]:
       //   IterationStatement[?Yield, ?Return]
@@ -6992,17 +6946,17 @@ Parser<ParseHandler>::statement(YieldHan
         }
         return returnStatement(yieldHandling);
 
       // WithStatement[?Yield, ?Return]
       case TOK_WITH:
         return withStatement(yieldHandling);
 
       // LabelledStatement[?Yield, ?Return]
-      // This is really handled by TOK_NAME and TOK_YIELD cases above.
+      // This is really handled by default and TOK_YIELD cases above.
 
       // ThrowStatement[?Yield]
       case TOK_THROW:
         return throwStatement(yieldHandling);
 
       // TryStatement[?Yield, ?Return]
       case TOK_TRY:
         return tryStatement(yieldHandling);
@@ -7098,51 +7052,50 @@ Parser<ParseHandler>::statementListItem(
             return null();
 
         if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
-      case TOK_NAME: {
+      default: {
+        // Avoid getting next token with None.
+        if (tt == TOK_AWAIT && pc->isAsync())
+            return expressionStatement(yieldHandling);
+
+        if (!TokenKindIsPossibleIdentifier(tt))
+            return expressionStatement(yieldHandling);
+
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
-        if (!tokenStream.currentToken().nameContainsEscape()) {
-            if (tokenStream.currentName() == context->names().let &&
-                nextTokenContinuesLetDeclaration(next, yieldHandling))
-            {
-                return lexicalDeclaration(yieldHandling, /* isConst = */ false);
-            }
-
-            if (tokenStream.currentName() == context->names().async) {
-                TokenKind nextSameLine = TOK_EOF;
-                if (!tokenStream.peekTokenSameLine(&nextSameLine))
-                    return null();
-                if (nextSameLine == TOK_FUNCTION) {
-                    tokenStream.consumeKnownToken(TOK_FUNCTION);
-                    return functionStmt(yieldHandling, NameRequired, AsyncFunction);
-                }
+        if (tt == TOK_LET && nextTokenContinuesLetDeclaration(next, yieldHandling))
+            return lexicalDeclaration(yieldHandling, /* isConst = */ false);
+
+        if (tt == TOK_ASYNC) {
+            TokenKind nextSameLine = TOK_EOF;
+            if (!tokenStream.peekTokenSameLine(&nextSameLine))
+                return null();
+            if (nextSameLine == TOK_FUNCTION) {
+                tokenStream.consumeKnownToken(TOK_FUNCTION);
+                return functionStmt(yieldHandling, NameRequired, AsyncFunction);
             }
         }
 
         if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NEW:
         return expressionStatement(yieldHandling, PredictInvoked);
 
-      default:
-        return expressionStatement(yieldHandling);
-
       // IfStatement[?Yield, ?Return]
       case TOK_IF:
         return ifStatement(yieldHandling);
 
       // BreakableStatement[?Yield, ?Return]
       //
       // BreakableStatement[Yield, Return]:
       //   IterationStatement[?Yield, ?Return]
@@ -7178,17 +7131,17 @@ Parser<ParseHandler>::statementListItem(
         }
         return returnStatement(yieldHandling);
 
       // WithStatement[?Yield, ?Return]
       case TOK_WITH:
         return withStatement(yieldHandling);
 
       // LabelledStatement[?Yield, ?Return]
-      // This is really handled by TOK_NAME and TOK_YIELD cases above.
+      // This is really handled by default and TOK_YIELD cases above.
 
       // ThrowStatement[?Yield]
       case TOK_THROW:
         return throwStatement(yieldHandling);
 
       // TryStatement[?Yield, ?Return]
       case TOK_TRY:
         return tryStatement(yieldHandling);
@@ -7544,16 +7497,19 @@ Parser<ParseHandler>::assignExpr(InHandl
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     uint32_t exprOffset = pos().begin;
 
     bool endsExpr;
 
+    // This only handles identifiers that *never* have special meaning anywhere
+    // in the language.  Contextual keywords, reserved words in strict mode,
+    // and other hard cases are handled outside this fast path.
     if (tt == TOK_NAME) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr) {
             Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
             if (!name)
                 return null();
 
@@ -7574,46 +7530,41 @@ Parser<ParseHandler>::assignExpr(InHandl
         if (endsExpr)
             return stringLiteral();
     }
 
     if (tt == TOK_YIELD && yieldExpressionsSupported())
         return yieldExpression(inHandling);
 
     bool maybeAsyncArrow = false;
-    if (tt == TOK_NAME &&
-        tokenStream.currentName() == context->names().async &&
-        !tokenStream.currentToken().nameContainsEscape())
-    {
+    if (tt == TOK_ASYNC) {
         TokenKind nextSameLine = TOK_EOF;
         if (!tokenStream.peekTokenSameLine(&nextSameLine))
             return null();
 
-        if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
+        if (TokenKindIsPossibleIdentifier(nextSameLine))
             maybeAsyncArrow = true;
     }
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
     PossibleError possibleErrorInner(*this);
     Node lhs;
     if (maybeAsyncArrow) {
-        tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
-        MOZ_ASSERT(tokenStream.currentName() == context->names().async);
-        MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
+        tokenStream.consumeKnownToken(TOK_ASYNC, TokenStream::Operand);
 
         TokenKind tt;
         if (!tokenStream.getToken(&tt))
             return null();
-        MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
+        MOZ_ASSERT(TokenKindIsPossibleIdentifier(tt));
 
         // Check yield validity here.
         RootedPropertyName name(context, bindingIdentifier(yieldHandling));
         if (!name)
             return null();
 
         if (!tokenStream.getToken(&tt))
             return null();
@@ -7670,34 +7621,28 @@ Parser<ParseHandler>::assignExpr(InHandl
         tokenStream.seek(start);
 
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
 
         GeneratorKind generatorKind = NotGenerator;
         FunctionAsyncKind asyncKind = SyncFunction;
 
-        if (next == TOK_NAME) {
+        if (next == TOK_ASYNC) {
             tokenStream.consumeKnownToken(next, TokenStream::Operand);
 
-            if (tokenStream.currentName() == context->names().async &&
-                !tokenStream.currentToken().nameContainsEscape())
-            {
-                TokenKind nextSameLine = TOK_EOF;
-                if (!tokenStream.peekTokenSameLine(&nextSameLine))
-                    return null();
-
-                if (nextSameLine == TOK_ARROW) {
-                    tokenStream.ungetToken();
-                } else {
-                    generatorKind = StarGenerator;
-                    asyncKind = AsyncFunction;
-                }
+            TokenKind nextSameLine = TOK_EOF;
+            if (!tokenStream.peekTokenSameLine(&nextSameLine))
+                return null();
+
+            if (nextSameLine == TOK_ARROW) {
+                tokenStream.ungetToken();
             } else {
-                tokenStream.ungetToken();
+                generatorKind = StarGenerator;
+                asyncKind = AsyncFunction;
             }
         }
 
         Node pn = handler.newArrowFunction();
         if (!pn)
             return null();
 
         Node arrowFunc = functionDefinition(pn, inHandling, yieldHandling, nullptr,
@@ -7936,30 +7881,27 @@ Parser<ParseHandler>::unaryExpr(YieldHan
 
             pc->sc()->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       case TOK_AWAIT: {
-        if (!pc->isAsync()) {
-            // TOK_AWAIT can be returned in module, even if it's not inside
-            // async function.
-            error(JSMSG_RESERVED_ID, "await");
-            return null();
-        }
-
-        Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
-        if (!kid)
-            return null();
-        pc->lastAwaitOffset = begin;
-        return newAwaitExpression(begin, kid);
+        if (pc->isAsync()) {
+            Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+            if (!kid)
+                return null();
+            pc->lastAwaitOffset = begin;
+            return newAwaitExpression(begin, kid);
+        }
       }
 
+        MOZ_FALLTHROUGH;
+
       default: {
         Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
                                possibleError, invoked);
         if (!expr)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
@@ -8085,28 +8027,28 @@ Parser<ParseHandler>::comprehensionFor(G
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     // FIXME: Destructuring binding (bug 980828).
 
-    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
+    MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifier, JSMSG_NO_VARIABLE_NAME);
     RootedPropertyName name(context, tokenStream.currentName());
     if (name == context->names().let) {
         error(JSMSG_LET_COMP_BINDING);
         return null();
     }
     TokenPos namePos = pos();
     Node lhs = newName(name);
     if (!lhs)
         return null();
     bool matched;
-    if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
+    if (!tokenStream.matchToken(&matched, TOK_OF))
         return null();
     if (!matched) {
         error(JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
     Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!rhs)
@@ -8431,19 +8373,19 @@ Parser<ParseHandler>::memberExpr(YieldHa
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_EOF)
             break;
 
         Node nextMember;
         if (tt == TOK_DOT) {
-            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
-                return null();
-            if (tt == TOK_NAME) {
+            if (!tokenStream.getToken(&tt))
+                return null();
+            if (TokenKindIsPossibleIdentifierName(tt)) {
                 PropertyName* field = tokenStream.currentName();
                 if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
                     error(JSMSG_BAD_SUPERPROP, "property");
                     return null();
                 }
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
@@ -8603,111 +8545,99 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
 {
     return handler.newName(name, pos, context);
 }
 
 template <typename ParseHandler>
 PropertyName*
-Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling,
-                                                 bool yieldTokenizedAsName)
-{
-    PropertyName* ident;
-    bool isYield;
-    const Token& tok = tokenStream.currentToken();
-    if (tok.type == TOK_NAME) {
-        MOZ_ASSERT(tok.name() != context->names().yield ||
-                   tok.nameContainsEscape() ||
-                   yieldTokenizedAsName,
-                   "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
-        MOZ_ASSERT_IF(yieldTokenizedAsName, tok.name() == context->names().yield);
-
-        ident = tok.name();
-        isYield = ident == context->names().yield;
-    } else {
-        MOZ_ASSERT(tok.type == TOK_YIELD && !yieldTokenizedAsName);
-
-        ident = context->names().yield;
-        isYield = true;
-    }
-
-    if (!isYield) {
-        if (pc->sc()->strict()) {
-            const char* badName = ident == context->names().let
-                                  ? "let"
-                                  : ident == context->names().static_
-                                  ? "static"
-                                  : nullptr;
-            if (badName) {
-                error(JSMSG_RESERVED_ID, badName);
-                return nullptr;
-            }
-        }
-    } else {
+Parser<ParseHandler>::checkLabelOrIdentifierReference(PropertyName* ident,
+                                                      uint32_t offset,
+                                                      YieldHandling yieldHandling)
+{
+    if (ident == context->names().yield) {
         if (yieldHandling == YieldIsKeyword ||
             pc->sc()->strict() ||
             versionNumber() >= JSVERSION_1_7)
         {
-            error(JSMSG_RESERVED_ID, "yield");
+            errorAt(offset, JSMSG_RESERVED_ID, "yield");
+            return nullptr;
+        }
+        return ident;
+    }
+
+    if (ident == context->names().await) {
+        if (awaitIsKeyword()) {
+            errorAt(offset, JSMSG_RESERVED_ID, "await");
+            return nullptr;
+        }
+        return ident;
+    }
+
+    if (IsKeyword(ident) || IsReservedWordLiteral(ident)) {
+        errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident));
+        return nullptr;
+    }
+
+    if (IsFutureReservedWord(ident)) {
+        errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident));
+        return nullptr;
+    }
+
+    if (pc->sc()->strict()) {
+        if (IsStrictReservedWord(ident)) {
+            errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident));
+            return nullptr;
+        }
+
+        if (ident == context->names().let) {
+            errorAt(offset, JSMSG_RESERVED_ID, "let");
+            return nullptr;
+        }
+
+        if (ident == context->names().static_) {
+            errorAt(offset, JSMSG_RESERVED_ID, "static");
             return nullptr;
         }
     }
 
     return ident;
 }
 
 template <typename ParseHandler>
 PropertyName*
+Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
+{
+    // ES 2017 draft 12.1.1.
+    //   StringValue of IdentifierName normalizes any Unicode escape sequences
+    //   in IdentifierName hence such escapes cannot be used to write an
+    //   Identifier whose code point sequence is the same as a ReservedWord.
+    //
+    // Use PropertyName* instead of TokenKind to reflect the normalization.
+
+    return checkLabelOrIdentifierReference(tokenStream.currentName(), pos().begin, yieldHandling);
+}
+
+template <typename ParseHandler>
+PropertyName*
 Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
 {
-    PropertyName* ident;
-    bool isYield;
-    const Token& tok = tokenStream.currentToken();
-    if (tok.type == TOK_NAME) {
-        MOZ_ASSERT(tok.name() != context->names().yield || tok.nameContainsEscape(),
-                   "tokenizer should have treated unescaped 'yield' as TOK_YIELD");
-
-        ident = tok.name();
-        isYield = ident == context->names().yield;
-    } else {
-        MOZ_ASSERT(tok.type == TOK_YIELD);
-
-        ident = context->names().yield;
-        isYield = true;
-    }
-
-    if (!isYield) {
-        if (pc->sc()->strict()) {
-            const char* badName = ident == context->names().arguments
-                                  ? "arguments"
-                                  : ident == context->names().eval
-                                  ? "eval"
-                                  : nullptr;
-            if (badName) {
-                error(JSMSG_BAD_STRICT_ASSIGN, badName);
-                return nullptr;
-            }
-
-            badName = ident == context->names().let
-                      ? "let"
-                      : ident == context->names().static_
-                      ? "static"
-                      : nullptr;
-            if (badName) {
-                error(JSMSG_RESERVED_ID, badName);
-                return nullptr;
-            }
-        }
-    } else {
-        if (yieldHandling == YieldIsKeyword ||
-            pc->sc()->strict() ||
-            versionNumber() >= JSVERSION_1_7)
-        {
-            error(JSMSG_RESERVED_ID, "yield");
+    PropertyName* ident = labelOrIdentifierReference(yieldHandling);
+    if (!ident)
+        return nullptr;
+
+    if (pc->sc()->strict()) {
+        if (ident == context->names().arguments) {
+            error(JSMSG_BAD_STRICT_ASSIGN, "arguments");
+            return nullptr;
+        }
+
+        if (ident == context->names().eval) {
+            error(JSMSG_BAD_STRICT_ASSIGN, "eval");
             return nullptr;
         }
     }
 
     return ident;
 }
 
 template <typename ParseHandler>
@@ -8878,42 +8808,38 @@ DoubleToAtom(JSContext* cx, double value
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
                                    PropertyType* propType, MutableHandleAtom propAtom)
 {
     TokenKind ltok;
-    if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+    if (!tokenStream.getToken(&ltok))
         return null();
 
     MOZ_ASSERT(ltok != TOK_RC, "caller should have handled TOK_RC");
 
     bool isGenerator = false;
     bool isAsync = false;
     if (ltok == TOK_MUL) {
         isGenerator = true;
-        if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
-            return null();
-    }
-
-    if (ltok == TOK_NAME &&
-        tokenStream.currentName() == context->names().async &&
-        !tokenStream.currentToken().nameContainsEscape())
-    {
+        if (!tokenStream.getToken(&ltok))
+            return null();
+    }
+
+    if (ltok == TOK_ASYNC) {
         TokenKind tt;
-        if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+        if (!tokenStream.getToken(&tt))
             return null();
         if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) {
             isAsync = true;
             ltok = tt;
         } else {
             tokenStream.ungetToken();
-            tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
         }
     }
 
     if (isAsync && isGenerator) {
         error(JSMSG_ASYNC_GENERATOR);
         return null();
     }
 
@@ -8930,88 +8856,76 @@ Parser<ParseHandler>::propertyName(Yield
         break;
 
       case TOK_LB:
         propName = computedPropertyName(yieldHandling, propList);
         if (!propName)
             return null();
         break;
 
-      case TOK_NAME: {
+      default: {
+        if (!TokenKindIsPossibleIdentifierName(ltok)) {
+            error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
+            return null();
+        }
+
         propAtom.set(tokenStream.currentName());
         // Do not look for accessor syntax on generators
-        if (isGenerator || isAsync ||
-            !(propAtom.get() == context->names().get ||
-              propAtom.get() == context->names().set))
-        {
+        if (isGenerator || isAsync || !(ltok == TOK_GET || ltok == TOK_SET)) {
             propName = handler.newObjectLiteralPropertyName(propAtom, pos());
             if (!propName)
                 return null();
             break;
         }
 
-        *propType = propAtom.get() == context->names().get ? PropertyType::Getter
-                                                           : PropertyType::Setter;
+        *propType = ltok == TOK_GET ? PropertyType::Getter : PropertyType::Setter;
 
         // We have parsed |get| or |set|. Look for an accessor property
         // name next.
         TokenKind tt;
-        if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
-            return null();
-        if (tt == TOK_NAME) {
-            if (!checkUnescapedName())
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName);
+        if (!tokenStream.peekToken(&tt))
+            return null();
+        if (TokenKindIsPossibleIdentifierName(tt)) {
+            tokenStream.consumeKnownToken(tt);
 
             propAtom.set(tokenStream.currentName());
             return handler.newObjectLiteralPropertyName(propAtom, pos());
         }
         if (tt == TOK_STRING) {
-            if (!checkUnescapedName())
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_STRING, TokenStream::KeywordIsName);
+            tokenStream.consumeKnownToken(TOK_STRING);
 
             propAtom.set(tokenStream.currentToken().atom());
 
             uint32_t index;
             if (propAtom->isIndex(&index)) {
                 propAtom.set(DoubleToAtom(context, index));
                 if (!propAtom.get())
                     return null();
                 return handler.newNumber(index, NoDecimal, pos());
             }
             return stringLiteral();
         }
         if (tt == TOK_NUMBER) {
-            if (!checkUnescapedName())
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_NUMBER, TokenStream::KeywordIsName);
+            tokenStream.consumeKnownToken(TOK_NUMBER);
 
             propAtom.set(DoubleToAtom(context, tokenStream.currentToken().number()));
             if (!propAtom.get())
                 return null();
             return newNumber(tokenStream.currentToken());
         }
         if (tt == TOK_LB) {
-            if (!checkUnescapedName())
-                return null();
-
-            tokenStream.consumeKnownToken(TOK_LB, TokenStream::KeywordIsName);
+            tokenStream.consumeKnownToken(TOK_LB);
 
             return computedPropertyName(yieldHandling, propList);
         }
 
         // Not an accessor property after all.
         propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
         if (!propName)
             return null();
-        tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
         break;
       }
 
       case TOK_STRING: {
         propAtom.set(tokenStream.currentToken().atom());
         uint32_t index;
         if (propAtom->isIndex(&index)) {
             propName = handler.newNumber(index, NoDecimal, pos());
@@ -9019,36 +8933,34 @@ Parser<ParseHandler>::propertyName(Yield
                 return null();
             break;
         }
         propName = stringLiteral();
         if (!propName)
             return null();
         break;
       }
-
-      default:
-        error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok));
-        return null();
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     if (tt == TOK_COLON) {
         if (isGenerator) {
             error(JSMSG_BAD_PROP_ID);
             return null();
         }
         *propType = PropertyType::Normal;
         return propName;
     }
 
-    if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN)) {
+    if (TokenKindIsPossibleIdentifierName(ltok) &&
+        (tt == TOK_COMMA || tt == TOK_RC || tt == TOK_ASSIGN))
+    {
         if (isGenerator) {
             error(JSMSG_BAD_PROP_ID);
             return null();
         }
         tokenStream.ungetToken();
         *propType = tt == TOK_ASSIGN ?
                           PropertyType::CoverInitializedName :
                           PropertyType::Shorthand;
@@ -9106,17 +9018,17 @@ Parser<ParseHandler>::objectLiteral(Yiel
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     bool seenCoverInitializedName = false;
     RootedAtom propAtom(context);
     for (;;) {
         TokenKind tt;
-        if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+        if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_RC)
             break;
 
         TokenPos namePos = pos();
 
         tokenStream.ungetToken();
 
@@ -9166,52 +9078,32 @@ Parser<ParseHandler>::objectLiteral(Yiel
                     return null();
             }
         } else if (propType == PropertyType::Shorthand) {
             /*
              * Support, e.g., |var {x, y} = o| as destructuring shorthand
              * for |var {x: x, y: y} = o|, and |var o = {x, y}| as initializer
              * shorthand for |var o = {x: x, y: y}|.
              */
-            TokenKind propToken = TOK_NAME;
-            if (!tokenStream.checkForKeyword(propAtom, &propToken))
-                return null();
-
-            if (propToken != TOK_NAME && propToken != TOK_YIELD) {
-                error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
-                return null();
-            }
-
-            Rooted<PropertyName*> name(context,
-                                       identifierReference(yieldHandling, propToken == TOK_YIELD));
+            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
             if (!name)
                 return null();
 
             Node nameExpr = identifierReference(name);
             if (!nameExpr)
                 return null();
 
             if (!handler.addShorthand(literal, propName, nameExpr))
                 return null();
         } else if (propType == PropertyType::CoverInitializedName) {
             /*
              * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
              * with default values, as per ES6 12.14.5
              */
-            TokenKind propToken = TOK_NAME;
-            if (!tokenStream.checkForKeyword(propAtom, &propToken))
-                return null();
-
-            if (propToken != TOK_NAME && propToken != TOK_YIELD) {
-                error(JSMSG_RESERVED_ID, TokenKindToDesc(propToken));
-                return null();
-            }
-
-            Rooted<PropertyName*> name(context,
-                                       identifierReference(yieldHandling, propToken == TOK_YIELD));
+            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
             if (!name)
                 return null();
 
             Node lhs = identifierReference(name);
             if (!lhs)
                 return null();
 
             tokenStream.consumeKnownToken(TOK_ASSIGN);
@@ -9372,24 +9264,21 @@ Parser<ParseHandler>::tryNewTarget(Node 
 
     // Don't unget the token, since lookahead cannot handle someone calling
     // getToken() with a different modifier. Callers should inspect currentToken().
     if (next != TOK_DOT)
         return true;
 
     if (!tokenStream.getToken(&next))
         return false;
-    if (next != TOK_NAME || tokenStream.currentName() != context->names().target) {
+    if (next != TOK_TARGET) {
         error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next));
         return false;
     }
 
-    if (!checkUnescapedName())
-        return false;
-
     if (!pc->sc()->allowNewTarget()) {
         errorAt(begin, JSMSG_BAD_NEWTARGET);
         return false;
     }
 
     Node targetHolder = handler.newPosHolder(pos());
     if (!targetHolder)
         return false;
@@ -9462,21 +9351,23 @@ Parser<ParseHandler>::primaryExpr(YieldH
         return templateLiteral(yieldHandling);
 
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionUntaggedTemplate();
 
       case TOK_STRING:
         return stringLiteral();
 
-      case TOK_YIELD:
-      case TOK_NAME: {
-        if (tokenStream.currentName() == context->names().async &&
-            !tokenStream.currentToken().nameContainsEscape())
-        {
+      default: {
+        if (!TokenKindIsPossibleIdentifier(tt)) {
+            error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
+            return null();
+        }
+
+        if (tt == TOK_ASYNC) {
             TokenKind nextSameLine = TOK_EOF;
             if (!tokenStream.peekTokenSameLine(&nextSameLine))
                 return null();
 
             if (nextSameLine == TOK_FUNCTION) {
                 tokenStream.consumeKnownToken(TOK_FUNCTION);
                 return functionExpr(PredictUninvoked, AsyncFunction);
             }
@@ -9537,17 +9428,17 @@ Parser<ParseHandler>::primaryExpr(YieldH
             {
                 return null();
             }
         } else {
             // This doesn't check that the provided name is allowed, e.g. if
             // the enclosing code is strict mode code, any of "let", "yield",
             // or "arguments" should be prohibited.  Argument-parsing code
             // handles that.
-            if (next != TOK_NAME && next != TOK_YIELD) {
+            if (!TokenKindIsPossibleIdentifier(next)) {
                 error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next));
                 return null();
             }
         }
 
         if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_RP) {
@@ -9564,20 +9455,16 @@ Parser<ParseHandler>::primaryExpr(YieldH
             return null();
         }
 
         tokenStream.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
-
-      default:
-        error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
-        return null();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
                                    TripledotHandling tripledotHandling,
                                    PossibleError* possibleError /* = nullptr */)
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -729,16 +729,19 @@ class UsedNameTracker
         map_.clear();
         RewindToken token;
         token.scriptId = 0;
         token.scopeId = 0;
         rewind(token);
     }
 };
 
+template <typename ParseHandler>
+class AutoAwaitIsKeyword;
+
 class ParserBase : public StrictModeGetter
 {
   private:
     ParserBase* thisForCtor() { return this; }
 
   public:
     JSContext* const context;
 
@@ -778,17 +781,23 @@ 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;
+
   public:
+    bool awaitIsKeyword() const {
+      return awaitIsKeyword_;
+    }
+
     ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser,
                LazyScript* lazyOuterFunction);
     ~ParserBase();
 
     const char* getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
@@ -992,16 +1001,19 @@ class Parser final : public ParserBase, 
     void freeTree(Node node) { handler.freeTree(node); }
 
   public:
     Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
            const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
            Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
     ~Parser();
 
+    friend class AutoAwaitIsKeyword<ParseHandler>;
+    void setAwaitIsKeyword(bool isKeyword);
+
     bool checkOptions();
 
     // A Parser::Mark is the extension of the LifoAlloc::Mark to the entire
     // Parser's state. Note: clients must still take care that any ParseContext
     // that points into released ParseNodes is destroyed.
     class Mark
     {
         friend class Parser;
@@ -1040,18 +1052,16 @@ class Parser final : public ParserBase, 
      * a function expression).
      */
     JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind,
                             GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
                             HandleObject proto);
 
     void trace(JSTracer* trc);
 
-    bool checkUnescapedName();
-
   private:
     Parser* thisForCtor() { return this; }
 
     JSAtom* stopStringCompression();
 
     Node stringLiteral();
     Node noSubstitutionTaggedTemplate();
     Node noSubstitutionUntaggedTemplate();
@@ -1311,27 +1321,28 @@ class Parser final : public ParserBase, 
     bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
     bool checkExportedName(JSAtom* exportName);
     bool checkExportedNamesForDeclaration(Node node);
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
 
-    PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling,
-                                             bool yieldTokenizedAsName);
+    PropertyName* checkLabelOrIdentifierReference(PropertyName* ident,
+                                                  uint32_t offset,
+                                                  YieldHandling yieldHandling);
+
+    PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
 
     PropertyName* labelIdentifier(YieldHandling yieldHandling) {
-        return labelOrIdentifierReference(yieldHandling, false);
+        return labelOrIdentifierReference(yieldHandling);
     }
 
-    PropertyName* identifierReference(YieldHandling yieldHandling,
-                                      bool yieldTokenizedAsName = false)
-    {
-        return labelOrIdentifierReference(yieldHandling, yieldTokenizedAsName);
+    PropertyName* identifierReference(YieldHandling yieldHandling) {
+        return labelOrIdentifierReference(yieldHandling);
     }
 
     PropertyName* importedBinding() {
         return bindingIdentifier(YieldIsName);
     }
 
     Node identifierReference(Handle<PropertyName*> name);
 
@@ -1443,12 +1454,31 @@ class Parser final : public ParserBase, 
 
     static Node null() { return ParseHandler::null(); }
 
     JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
 
     bool asmJS(Node list);
 };
 
+template <typename ParseHandler>
+class MOZ_STACK_CLASS AutoAwaitIsKeyword
+{
+  private:
+    Parser<ParseHandler>* parser_;
+    bool oldAwaitIsKeyword_;
+
+  public:
+    AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) {
+        parser_ = parser;
+        oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
+        parser_->setAwaitIsKeyword(awaitIsKeyword);
+    }
+
+    ~AutoAwaitIsKeyword() {
+        parser_->setAwaitIsKeyword(oldAwaitIsKeyword_);
+    }
+};
+
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_Parser_h */
rename from js/src/vm/Keywords.h
rename to js/src/frontend/ReservedWords.h
--- a/js/src/vm/Keywords.h
+++ b/js/src/frontend/ReservedWords.h
@@ -1,23 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* A higher-order macro for enumerating keyword tokens. */
+/* A higher-order macro for enumerating reserved word tokens. */
 
-#ifndef vm_Keywords_h
-#define vm_Keywords_h
+#ifndef vm_ReservedWords_h
+#define vm_ReservedWords_h
 
-#define FOR_EACH_JAVASCRIPT_KEYWORD(macro) \
+#define FOR_EACH_JAVASCRIPT_RESERVED_WORD(macro) \
     macro(false, false_, TOK_FALSE) \
     macro(true, true_, TOK_TRUE) \
     macro(null, null, TOK_NULL) \
+    \
     /* Keywords. */ \
     macro(break, break_, TOK_BREAK) \
     macro(case, case_, TOK_CASE) \
     macro(catch, catch_, TOK_CATCH) \
     macro(const, const_, TOK_CONST) \
     macro(continue, continue_, TOK_CONTINUE) \
     macro(debugger, debugger, TOK_DEBUGGER) \
     macro(default, default_, TOK_DEFAULT) \
@@ -31,36 +32,50 @@
     macro(in, in, TOK_IN) \
     macro(instanceof, instanceof, TOK_INSTANCEOF) \
     macro(new, new_, TOK_NEW) \
     macro(return, return_, TOK_RETURN) \
     macro(switch, switch_, TOK_SWITCH) \
     macro(this, this_, TOK_THIS) \
     macro(throw, throw_, TOK_THROW) \
     macro(try, try_, TOK_TRY) \
-    macro(typeof, typeof, TOK_TYPEOF) \
+    macro(typeof, typeof_, TOK_TYPEOF) \
     macro(var, var, TOK_VAR) \
     macro(void, void_, TOK_VOID) \
     macro(while, while_, TOK_WHILE) \
     macro(with, with, TOK_WITH) \
     macro(import, import, TOK_IMPORT) \
-    macro(export, export, TOK_EXPORT) \
+    macro(export, export_, TOK_EXPORT) \
     macro(class, class_, TOK_CLASS) \
     macro(extends, extends, TOK_EXTENDS) \
     macro(super, super, TOK_SUPER) \
-    /* Reserved keywords. */ \
-    macro(enum, enum_, TOK_RESERVED) \
-    /* Future reserved keywords, but only in strict mode. */ \
-    macro(implements, implements, TOK_STRICT_RESERVED) \
-    macro(interface, interface, TOK_STRICT_RESERVED) \
-    macro(package, package, TOK_STRICT_RESERVED) \
-    macro(private, private_, TOK_STRICT_RESERVED) \
-    macro(protected, protected_, TOK_STRICT_RESERVED) \
-    macro(public, public_, TOK_STRICT_RESERVED) \
+    \
+    /* Future reserved words. */ \
+    macro(enum, enum_, TOK_ENUM) \
+    \
+    /* Future reserved words, but only in strict mode. */ \
+    macro(implements, implements, TOK_IMPLEMENTS) \
+    macro(interface, interface, TOK_INTERFACE) \
+    macro(package, package, TOK_PACKAGE) \
+    macro(private, private_, TOK_PRIVATE) \
+    macro(protected, protected_, TOK_PROTECTED) \
+    macro(public, public_, TOK_PUBLIC) \
+    \
+    /* Contextual keywords. */ \
+    macro(as, as, TOK_AS) \
+    macro(async, async, TOK_ASYNC) \
     macro(await, await, TOK_AWAIT) \
+    macro(each, each, TOK_EACH) \
+    macro(from, from, TOK_FROM) \
+    macro(get, get, TOK_GET) \
+    macro(let, let, TOK_LET) \
+    macro(of, of, TOK_OF) \
+    macro(set, set, TOK_SET) \
+    macro(static, static_, TOK_STATIC) \
+    macro(target, target, TOK_TARGET) \
     /* \
      * Yield is a token inside function*.  Outside of a function*, it is a \
-     * future reserved keyword in strict mode, but a keyword in JS1.7 even \
+     * future reserved word in strict mode, but a keyword in JS1.7 even \
      * when strict.  Punt logic to parser. \
      */ \
     macro(yield, yield, TOK_YIELD)
 
-#endif /* vm_Keywords_h */
+#endif /* vm_ReservedWords_h */
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -76,19 +76,22 @@
     \
     /* start of template literal with substitutions */ \
     macro(TEMPLATE_HEAD,    "'${'") \
     /* template literal without substitutions */ \
     macro(NO_SUBS_TEMPLATE, "template literal") \
     \
     macro(REGEXP,       "regular expression literal") \
     macro(TRUE,         "boolean literal 'true'") \
+    range(RESERVED_WORD_LITERAL_FIRST, TRUE) \
     macro(FALSE,        "boolean literal 'false'") \
     macro(NULL,         "null literal") \
+    range(RESERVED_WORD_LITERAL_LAST, NULL) \
     macro(THIS,         "keyword 'this'") \
+    range(KEYWORD_FIRST, THIS) \
     macro(FUNCTION,     "keyword 'function'") \
     macro(IF,           "keyword 'if'") \
     macro(ELSE,         "keyword 'else'") \
     macro(SWITCH,       "keyword 'switch'") \
     macro(CASE,         "keyword 'case'") \
     macro(DEFAULT,      "keyword 'default'") \
     macro(WHILE,        "keyword 'while'") \
     macro(DO,           "keyword 'do'") \
@@ -101,26 +104,53 @@
     macro(RETURN,       "keyword 'return'") \
     macro(NEW,          "keyword 'new'") \
     macro(DELETE,       "keyword 'delete'") \
     macro(TRY,          "keyword 'try'") \
     macro(CATCH,        "keyword 'catch'") \
     macro(FINALLY,      "keyword 'finally'") \
     macro(THROW,        "keyword 'throw'") \
     macro(DEBUGGER,     "keyword 'debugger'") \
-    macro(YIELD,        "keyword 'yield'") \
-    macro(AWAIT,        "keyword 'await'") \
     macro(EXPORT,       "keyword 'export'") \
     macro(IMPORT,       "keyword 'import'") \
     macro(CLASS,        "keyword 'class'") \
     macro(EXTENDS,      "keyword 'extends'") \
     macro(SUPER,        "keyword 'super'") \
-    macro(RESERVED,     "reserved keyword") \
-    /* reserved keywords in strict mode */ \
-    macro(STRICT_RESERVED, "reserved keyword") \
+    range(KEYWORD_LAST, SUPER) \
+    \
+    /* contextual keywords */ \
+    macro(AS,           "'as'") \
+    range(CONTEXTUAL_KEYWORD_FIRST, AS) \
+    macro(ASYNC,        "'async'") \
+    macro(AWAIT,        "'await'") \
+    macro(EACH,         "'each'") \
+    macro(FROM,         "'from'") \
+    macro(GET,          "'get'") \
+    macro(LET,          "'let'") \
+    macro(OF,           "'of'") \
+    macro(SET,          "'set'") \
+    macro(STATIC,       "'static'") \
+    macro(TARGET,       "'target'") \
+    macro(YIELD,        "'yield'") \
+    range(CONTEXTUAL_KEYWORD_LAST, YIELD) \
+    \
+    /* future reserved words */ \
+    macro(ENUM,         "reserved word 'enum'") \
+    range(FUTURE_RESERVED_KEYWORD_FIRST, ENUM) \
+    range(FUTURE_RESERVED_KEYWORD_LAST, ENUM) \
+    \
+    /* reserved words in strict mode */ \
+    macro(IMPLEMENTS,   "reserved word 'implements'") \
+    range(STRICT_RESERVED_KEYWORD_FIRST, IMPLEMENTS) \
+    macro(INTERFACE,    "reserved word 'interface'") \
+    macro(PACKAGE,      "reserved word 'package'") \
+    macro(PRIVATE,      "reserved word 'private'") \
+    macro(PROTECTED,    "reserved word 'protected'") \
+    macro(PUBLIC,       "reserved word 'public'") \
+    range(STRICT_RESERVED_KEYWORD_LAST, PUBLIC) \
     \
     /* \
      * The following token types occupy contiguous ranges to enable easy \
      * range-testing. \
      */ \
     /* \
      * Binary operators tokens, TOK_OR thru TOK_POW. These must be in the same \
      * order as F(OR) and friends in FOR_EACH_PARSE_NODE_KIND in ParseNode.h. \
@@ -144,17 +174,19 @@
     macro(LT,           "'<'") \
     range(RELOP_START, LT) \
     macro(LE,           "'<='") \
     macro(GT,           "'>'") \
     macro(GE,           "'>='") \
     range(RELOP_LAST, GE) \
     \
     macro(INSTANCEOF,   "keyword 'instanceof'") \
+    range(KEYWORD_BINOP_FIRST, INSTANCEOF) \
     macro(IN,           "keyword 'in'") \
+    range(KEYWORD_BINOP_LAST, IN) \
     \
     /* Shift ops, per TokenKindIsShift. */ \
     macro(LSH,          "'<<'") \
     range(SHIFTOP_START, LSH) \
     macro(RSH,          "'>>'") \
     macro(URSH,         "'>>>'") \
     range(SHIFTOP_LAST, URSH) \
     \
@@ -163,17 +195,19 @@
     macro(MUL,          "'*'") \
     macro(DIV,          "'/'") \
     macro(MOD,          "'%'") \
     macro(POW,          "'**'") \
     range(BINOP_LAST, POW) \
     \
     /* Unary operation tokens. */ \
     macro(TYPEOF,       "keyword 'typeof'") \
+    range(KEYWORD_UNOP_FIRST, TYPEOF) \
     macro(VOID,         "keyword 'void'") \
+    range(KEYWORD_UNOP_LAST, VOID) \
     macro(NOT,          "'!'") \
     macro(BITNOT,       "'~'") \
     \
     macro(ARROW,        "'=>'")   /* function arrow */ \
     \
     /* Assignment ops, per TokenKindIsAssignment */ \
     macro(ASSIGN,       "'='") \
     range(ASSIGNMENT_START, ASSIGN) \
@@ -234,12 +268,67 @@ TokenKindIsShift(TokenKind tt)
 }
 
 inline bool
 TokenKindIsAssignment(TokenKind tt)
 {
     return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
 }
 
+inline MOZ_MUST_USE bool
+TokenKindIsKeyword(TokenKind tt)
+{
+    return (TOK_KEYWORD_FIRST <= tt && tt <= TOK_KEYWORD_LAST) ||
+           (TOK_KEYWORD_BINOP_FIRST <= tt && tt <= TOK_KEYWORD_BINOP_LAST) ||
+           (TOK_KEYWORD_UNOP_FIRST <= tt && tt <= TOK_KEYWORD_UNOP_LAST);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsContextualKeyword(TokenKind tt)
+{
+    return TOK_CONTEXTUAL_KEYWORD_FIRST <= tt && tt <= TOK_CONTEXTUAL_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsFutureReservedWord(TokenKind tt)
+{
+    return TOK_FUTURE_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_FUTURE_RESERVED_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsStrictReservedWord(TokenKind tt)
+{
+    return TOK_STRICT_RESERVED_KEYWORD_FIRST <= tt && tt <= TOK_STRICT_RESERVED_KEYWORD_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsReservedWordLiteral(TokenKind tt)
+{
+    return TOK_RESERVED_WORD_LITERAL_FIRST <= tt && tt <= TOK_RESERVED_WORD_LITERAL_LAST;
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsReservedWord(TokenKind tt)
+{
+    return TokenKindIsKeyword(tt) ||
+           TokenKindIsFutureReservedWord(tt) ||
+           TokenKindIsReservedWordLiteral(tt);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsPossibleIdentifier(TokenKind tt)
+{
+    return tt == TOK_NAME ||
+           TokenKindIsContextualKeyword(tt) ||
+           TokenKindIsStrictReservedWord(tt);
+}
+
+inline MOZ_MUST_USE bool
+TokenKindIsPossibleIdentifierName(TokenKind tt)
+{
+    return TokenKindIsPossibleIdentifier(tt) ||
+           TokenKindIsReservedWord(tt);
+}
+
 } // namespace frontend
 } // namespace js
 
 #endif /* frontend_TokenKind_h */
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -19,91 +19,91 @@
 
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsexn.h"
 #include "jsnum.h"
 
 #include "frontend/BytecodeCompiler.h"
+#include "frontend/ReservedWords.h"
 #include "js/CharacterEncoding.h"
 #include "js/UniquePtr.h"
 #include "vm/HelperThreads.h"
-#include "vm/Keywords.h"
 #include "vm/StringBuffer.h"
 #include "vm/Unicode.h"
 
 using namespace js;
 using namespace js::frontend;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::PodAssign;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
-struct KeywordInfo {
-    const char* chars;         // C string with keyword text
+struct ReservedWordInfo {
+    const char* chars;         // C string with reserved word text
     TokenKind   tokentype;
 };
 
-static const KeywordInfo keywords[] = {
-#define KEYWORD_INFO(keyword, name, type) \
-    {js_##keyword##_str, type},
-    FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_INFO)
-#undef KEYWORD_INFO
+static const ReservedWordInfo reservedWords[] = {
+#define RESERVED_WORD_INFO(word, name, type) \
+    {js_##word##_str, type},
+    FOR_EACH_JAVASCRIPT_RESERVED_WORD(RESERVED_WORD_INFO)
+#undef RESERVED_WORD_INFO
 };
 
-// Returns a KeywordInfo for the specified characters, or nullptr if the string
-// is not a keyword.
+// Returns a ReservedWordInfo for the specified characters, or nullptr if the
+// string is not a reserved word.
 template <typename CharT>
-static const KeywordInfo*
-FindKeyword(const CharT* s, size_t length)
+static const ReservedWordInfo*
+FindReservedWord(const CharT* s, size_t length)
 {
     MOZ_ASSERT(length != 0);
 
     size_t i;
-    const KeywordInfo* kw;
+    const ReservedWordInfo* rw;
     const char* chars;
 
-#define JSKW_LENGTH()           length
-#define JSKW_AT(column)         s[column]
-#define JSKW_GOT_MATCH(index)   i = (index); goto got_match;
-#define JSKW_TEST_GUESS(index)  i = (index); goto test_guess;
-#define JSKW_NO_MATCH()         goto no_match;
-#include "jsautokw.h"
-#undef JSKW_NO_MATCH
-#undef JSKW_TEST_GUESS
-#undef JSKW_GOT_MATCH
-#undef JSKW_AT
-#undef JSKW_LENGTH
+#define JSRW_LENGTH()           length
+#define JSRW_AT(column)         s[column]
+#define JSRW_GOT_MATCH(index)   i = (index); goto got_match;
+#define JSRW_TEST_GUESS(index)  i = (index); goto test_guess;
+#define JSRW_NO_MATCH()         goto no_match;
+#include "frontend/ReservedWordsGenerated.h"
+#undef JSRW_NO_MATCH
+#undef JSRW_TEST_GUESS
+#undef JSRW_GOT_MATCH
+#undef JSRW_AT
+#undef JSRW_LENGTH
 
   got_match:
-    return &keywords[i];
+    return &reservedWords[i];
 
   test_guess:
-    kw = &keywords[i];
-    chars = kw->chars;
+    rw = &reservedWords[i];
+    chars = rw->chars;
     do {
         if (*s++ != (unsigned char)(*chars++))
             goto no_match;
     } while (--length != 0);
-    return kw;
+    return rw;
 
   no_match:
     return nullptr;
 }
 
-static const KeywordInfo*
-FindKeyword(JSLinearString* str)
+static const ReservedWordInfo*
+FindReservedWord(JSLinearString* str)
 {
     JS::AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
-           ? FindKeyword(str->latin1Chars(nogc), str->length())
-           : FindKeyword(str->twoByteChars(nogc), str->length());
+           ? FindReservedWord(str->latin1Chars(nogc), str->length())
+           : FindReservedWord(str->twoByteChars(nogc), str->length());
 }
 
 template <typename CharT>
 static bool
 IsIdentifier(const CharT* chars, size_t length)
 {
     if (length == 0)
         return false;
@@ -177,17 +177,78 @@ bool
 frontend::IsIdentifier(const char16_t* chars, size_t length)
 {
     return ::IsIdentifier(chars, length);
 }
 
 bool
 frontend::IsKeyword(JSLinearString* str)
 {
-    return FindKeyword(str) != nullptr;
+    if (const ReservedWordInfo* rw = FindReservedWord(str))
+        return TokenKindIsKeyword(rw->tokentype);
+
+    return false;
+}
+
+bool
+frontend::IsFutureReservedWord(JSLinearString* str)
+{
+    if (const ReservedWordInfo* rw = FindReservedWord(str))
+        return TokenKindIsFutureReservedWord(rw->tokentype);
+
+    return false;
+}
+
+bool
+frontend::IsStrictReservedWord(JSLinearString* str)
+{
+    if (const ReservedWordInfo* rw = FindReservedWord(str))
+        return TokenKindIsStrictReservedWord(rw->tokentype);
+
+    return false;
+}
+
+bool
+frontend::IsReservedWordLiteral(JSLinearString* str)
+{
+    if (const ReservedWordInfo* rw = FindReservedWord(str))
+        return TokenKindIsReservedWordLiteral(rw->tokentype);
+
+    return false;
+}
+
+const char*
+frontend::ReservedWordToCharZ(PropertyName* str)
+{
+    const ReservedWordInfo* rw = FindReservedWord(str);
+    if (rw == nullptr)
+        return nullptr;
+
+    switch (rw->tokentype) {
+#define EMIT_CASE(word, name, type) case type: return js_##word##_str;
+      FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
+#undef EMIT_CASE
+      default:
+        MOZ_ASSERT_UNREACHABLE("Not a reserved word PropertyName.");
+    }
+    return nullptr;
+}
+
+PropertyName*
+TokenStream::reservedWordToPropertyName(TokenKind tt) const
+{
+    MOZ_ASSERT(tt != TOK_NAME);
+    switch (tt) {
+#define EMIT_CASE(word, name, type) case type: return cx->names().name;
+      FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE)
+#undef EMIT_CASE
+      default:
+        MOZ_ASSERT_UNREACHABLE("Not a reserved word TokenKind.");
+    }
+    return nullptr;
 }
 
 TokenStream::SourceCoords::SourceCoords(JSContext* cx, uint32_t ln)
   : lineStartOffsets_(cx), initialLineNum_(ln), lastLineIndex_(0)
 {
     // This is actually necessary!  Removing it causes compile errors on
     // GCC and clang.  You could try declaring this:
     //
@@ -1154,48 +1215,16 @@ TokenStream::putIdentInTokenbuf(const ch
             userbuf.setAddressOfNextRawChar(tmp);
             return false;
         }
     }
     userbuf.setAddressOfNextRawChar(tmp);
     return true;
 }
 
-bool
-TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
-{
-    if (!awaitIsKeyword && kw->tokentype == TOK_AWAIT) {
-        if (ttp)
-            *ttp = TOK_NAME;
-        return true;
-    }
-
-    if (kw->tokentype == TOK_RESERVED) {
-        error(JSMSG_RESERVED_ID, kw->chars);
-        return false;
-    }
-
-    if (kw->tokentype == TOK_STRICT_RESERVED)
-        return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
-
-    // Working keyword.
-    *ttp = kw->tokentype;
-    return true;
-}
-
-bool
-TokenStream::checkForKeyword(JSAtom* atom, TokenKind* ttp)
-{
-    const KeywordInfo* kw = FindKeyword(atom);
-    if (!kw)
-        return true;
-
-    return checkForKeyword(kw, ttp);
-}
-
 enum FirstCharKind {
     // A char16_t has the 'OneChar' kind if it, by itself, constitutes a valid
     // token that cannot also be a prefix of a longer token.  E.g. ';' has the
     // OneChar kind, but '+' does not, because '++' and '+=' are valid longer tokens
     // that begin with '+'.
     //
     // The few token kinds satisfying these properties cover roughly 35--45%
     // of the tokens seen in practice.
@@ -1409,40 +1438,21 @@ TokenStream::getTokenInternal(TokenKind*
 
             chars = tokenbuf.begin();
             length = tokenbuf.length();
         } else {
             chars = identStart;
             length = userbuf.addressOfNextRawChar() - identStart;
         }
 
-        // Represent keywords as keyword tokens unless told otherwise.
-        if (modifier != KeywordIsName) {
-            if (const KeywordInfo* kw = FindKeyword(chars, length)) {
-                // That said, keywords can't contain escapes.  (Contexts where
-                // keywords are treated as names, that also sometimes treat
-                // keywords as keywords, must manually check this requirement.)
-                // There are two exceptions
-                // 1) StrictReservedWords: These keywords need to be treated as
-                //    names in non-strict mode.
-                // 2) yield is also treated as a name if it contains an escape
-                //    sequence. The parser must handle this case separately.
-                if (hadUnicodeEscape && !(
-                        (kw->tokentype == TOK_STRICT_RESERVED && !strictMode()) ||
-                         kw->tokentype == TOK_YIELD))
-                {
-                    reportError(JSMSG_ESCAPED_KEYWORD);
-                    goto error;
-                }
-
-                tp->type = TOK_NAME;
-                if (!checkForKeyword(kw, &tp->type))
-                    goto error;
-                if (tp->type != TOK_NAME && !hadUnicodeEscape)
-                    goto out;
+        // Represent reserved words as reserved word tokens.
+        if (!hadUnicodeEscape) {
+            if (const ReservedWordInfo* rw = FindReservedWord(chars, length)) {
+                tp->type = rw->tokentype;
+                goto out;
             }
         }
 
         JSAtom* atom = AtomizeChars(cx, chars, length);
         if (!atom)
             goto error;
         tp->type = TOK_NAME;
         tp->setName(atom->asPropertyName());
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -21,24 +21,23 @@
 
 #include "jscntxt.h"
 #include "jspubtd.h"
 
 #include "frontend/TokenKind.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "vm/RegExpObject.h"
+#include "vm/String.h"
 
 struct KeywordInfo;
 
 namespace js {
 namespace frontend {
 
-class AutoAwaitIsKeyword;
-
 struct TokenPos {
     uint32_t    begin;  // Offset of the token's first char.
     uint32_t    end;    // Offset of 1 past the token's last char.
 
     TokenPos() {}
     TokenPos(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
 
     // Return a TokenPos that covers left, right, and anything in between.
@@ -115,19 +114,16 @@ struct Token
         // Normal operation.
         None,
 
         // Looking for an operand, not an operator.  In practice, this means
         // that when '/' is seen, we look for a regexp instead of just returning
         // TOK_DIV.
         Operand,
 
-        // Treat keywords as names by returning TOK_NAME.
-        KeywordIsName,
-
         // Treat subsequent characters as the tail of a template literal, after
         // a template substitution, beginning with a "}", continuing with zero
         // or more template literal characters, and ending with either "${" or
         // the end of the template literal.  For example:
         //
         //   var entity = "world";
         //   var s = `Hello ${entity}!`;
         //                          ^ TemplateTail context
@@ -159,20 +155,16 @@ struct Token
         // '/' is the first character).  So we should peek the token as
         // operand before try getting colon/comma/semicolon.
         // See also the comment in Parser::assignExpr().
         NoneIsOperand,
 
         // If a semicolon is inserted automatically, the next token is already
         // gotten with None, but we expect Operand.
         OperandIsNone,
-
-        // If name of method definition is `get` or `set`, the next token is
-        // already gotten with KeywordIsName, but we expect None.
-        NoneIsKeywordIsName,
     };
     friend class TokenStream;
 
   public:
     TokenKind           type;           // char value or above enumerator
     TokenPos            pos;            // token position in file
     union {
       private:
@@ -219,21 +211,16 @@ struct Token
 
     // Type-safe accessors
 
     PropertyName* name() const {
         MOZ_ASSERT(type == TOK_NAME);
         return u.name->JSAtom::asPropertyName(); // poor-man's type verification
     }
 
-    bool nameContainsEscape() const {
-        PropertyName* n = name();
-        return pos.begin + n->length() != pos.end;
-    }
-
     JSAtom* atom() const {
         MOZ_ASSERT(type == TOK_STRING ||
                    type == TOK_TEMPLATE_HEAD ||
                    type == TOK_NO_SUBS_TEMPLATE);
         return u.atom;
     }
 
     js::RegExpFlag regExpFlags() const {
@@ -249,20 +236,32 @@ struct Token
 
     DecimalPoint decimalPoint() const {
         MOZ_ASSERT(type == TOK_NUMBER);
         return u.number.decimalPoint;
     }
 };
 
 class CompileError : public JSErrorReport {
-public:
+  public:
     void throwError(JSContext* cx);
 };
 
+extern const char*
+ReservedWordToCharZ(PropertyName* str);
+
+extern MOZ_MUST_USE bool
+IsFutureReservedWord(JSLinearString* str);
+
+extern MOZ_MUST_USE bool
+IsReservedWordLiteral(JSLinearString* str);
+
+extern MOZ_MUST_USE bool
+IsStrictReservedWord(JSLinearString* str);
+
 // Ideally, tokenizing would be entirely independent of context.  But the
 // strict mode flag, which is in SharedContext, affects tokenizing, and
 // TokenStream needs to see it.
 //
 // This class is a tiny back-channel from TokenStream to the strict mode flag
 // that avoids exposing the rest of SharedContext to TokenStream.
 //
 class StrictModeGetter {
@@ -339,35 +338,34 @@ class MOZ_STACK_CLASS TokenStream
         return currentToken().type == type;
     }
     const CharBuffer& getTokenbuf() const { return tokenbuf; }
     const char* getFilename() const { return filename; }
     bool getMutedErrors() const { return mutedErrors; }
     JSVersion versionNumber() const { return VersionNumber(options().version); }
     JSVersion versionWithFlags() const { return options().version; }
 
+  private:
+    PropertyName* reservedWordToPropertyName(TokenKind tt) const;
+
+  public:
     PropertyName* currentName() const {
-        if (isCurrentTokenType(TOK_YIELD))
-            return cx->names().yield;
-        MOZ_ASSERT(isCurrentTokenType(TOK_NAME));
-        return currentToken().name();
+        if (isCurrentTokenType(TOK_NAME))
+            return currentToken().name();
+
+        MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type));
+        return reservedWordToPropertyName(currentToken().type);
     }
 
     PropertyName* nextName() const {
-        if (nextToken().type == TOK_YIELD)
-            return cx->names().yield;
-        MOZ_ASSERT(nextToken().type == TOK_NAME);
-        return nextToken().name();
-    }
+        if (nextToken().type != TOK_NAME)
+            return nextToken().name();
 
-    bool nextNameContainsEscape() const {
-        if (nextToken().type == TOK_YIELD)
-            return false;
-        MOZ_ASSERT(nextToken().type == TOK_NAME);
-        return nextToken().nameContainsEscape();
+        MOZ_ASSERT(TokenKindIsPossibleIdentifierName(nextToken().type));
+        return reservedWordToPropertyName(nextToken().type);
     }
 
     bool isCurrentTokenAssignment() const {
         return TokenKindIsAssignment(currentToken().type);
     }
 
     // Flag methods.
     bool isEOF() const { return flags.isEOF; }
@@ -492,34 +490,29 @@ class MOZ_STACK_CLASS TokenStream
                                 // token.
         bool hitOOM:1;          // Hit OOM.
 
         Flags()
           : isEOF(), isDirtyLine(), sawOctalEscape(), hadError(), hitOOM()
         {}
     };
 
-    bool awaitIsKeyword = false;
-    friend class AutoAwaitIsKeyword;
-
     uint32_t invalidTemplateEscapeOffset = 0;
     InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None;
 
   public:
     typedef Token::Modifier Modifier;
     static constexpr Modifier None = Token::None;
     static constexpr Modifier Operand = Token::Operand;
-    static constexpr Modifier KeywordIsName = Token::KeywordIsName;
     static constexpr Modifier TemplateTail = Token::TemplateTail;
 
     typedef Token::ModifierException ModifierException;
     static constexpr ModifierException NoException = Token::NoException;
     static constexpr ModifierException NoneIsOperand = Token::NoneIsOperand;
     static constexpr ModifierException OperandIsNone = Token::OperandIsNone;
-    static constexpr ModifierException NoneIsKeywordIsName = Token::NoneIsKeywordIsName;
 
     void addModifierException(ModifierException modifierException) {
 #ifdef DEBUG
         const Token& next = nextToken();
         if (next.modifierException == NoneIsOperand)
         {
             // Token after yield expression without operand already has
             // NoneIsOperand exception.
@@ -538,20 +531,16 @@ class MOZ_STACK_CLASS TokenStream
             MOZ_ASSERT(next.type != TOK_DIV,
                        "next token requires contextual specifier to be parsed unambiguously");
             break;
           case OperandIsNone:
             MOZ_ASSERT(next.modifier == None);
             MOZ_ASSERT(next.type != TOK_DIV && next.type != TOK_REGEXP,
                        "next token requires contextual specifier to be parsed unambiguously");
             break;
-          case NoneIsKeywordIsName:
-            MOZ_ASSERT(next.modifier == KeywordIsName);
-            MOZ_ASSERT(next.type != TOK_NAME);
-            break;
           default:
             MOZ_CRASH("unexpected modifier exception");
         }
         tokens[(cursor + 1) & ntokensMask].modifierException = modifierException;
 #endif
     }
 
     void
@@ -568,22 +557,16 @@ class MOZ_STACK_CLASS TokenStream
         }
 
         if (lookaheadToken.modifierException == NoneIsOperand) {
             // getToken() permissibly following getToken(Operand).
             if (modifier == None && lookaheadToken.modifier == Operand)
                 return;
         }
 
-        if (lookaheadToken.modifierException == NoneIsKeywordIsName) {
-            // getToken() permissibly following getToken(KeywordIsName).
-            if (modifier == None && lookaheadToken.modifier == KeywordIsName)
-                return;
-        }
-
         MOZ_ASSERT_UNREACHABLE("this token was previously looked up with a "
                                "different modifier, potentially making "
                                "tokenization non-deterministic");
 #endif
     }
 
     // Advance to the next token.  If the token stream encountered an error,
     // return false.  Otherwise return true and store the token kind in |*ttp|.
@@ -708,46 +691,16 @@ class MOZ_STACK_CLASS TokenStream
 
     void consumeKnownToken(TokenKind tt, Modifier modifier = None) {
         bool matched;
         MOZ_ASSERT(hasLookahead());
         MOZ_ALWAYS_TRUE(matchToken(&matched, tt, modifier));
         MOZ_ALWAYS_TRUE(matched);
     }
 
-    // Like matchToken(..., TOK_NAME) but further matching the name token only
-    // if it has the given characters, without containing escape sequences.
-    // If the name token has the given characters yet *does* contain an escape,
-    // a syntax error will be reported.
-    //
-    // This latter behavior makes this method unsuitable for use in any context
-    // where ASI might occur.  In such places, an escaped "contextual keyword"
-    // on a new line is the start of an ExpressionStatement, not a continuation
-    // of a StatementListItem (or ImportDeclaration or ExportDeclaration, in
-    // modules).
-    MOZ_MUST_USE bool matchContextualKeyword(bool* matchedp, Handle<PropertyName*> keyword,
-                                             Modifier modifier = None)
-    {
-        TokenKind token;
-        if (!getToken(&token, modifier))
-            return false;
-        if (token == TOK_NAME && currentToken().name() == keyword) {
-            if (currentToken().nameContainsEscape()) {
-                reportError(JSMSG_ESCAPED_KEYWORD);
-                return false;
-            }
-
-            *matchedp = true;
-        } else {
-            *matchedp = false;
-            ungetToken();
-        }
-        return true;
-    }
-
     MOZ_MUST_USE bool nextTokenEndsExpr(bool* endsExpr) {
         TokenKind tt;
         if (!peekToken(&tt))
             return false;
         *endsExpr = isExprEnding[tt];
         return true;
     }
 
@@ -803,29 +756,16 @@ class MOZ_STACK_CLASS TokenStream
     bool hasSourceMapURL() const {
         return sourceMapURL_ != nullptr;
     }
 
     char16_t* sourceMapURL() {
         return sourceMapURL_.get();
     }
 
-    // If |atom| is not a keyword in this version, return true with *ttp
-    // unchanged.
-    //
-    // If it is a reserved word in this version and strictness mode, and thus
-    // can't be present in correct code, report a SyntaxError and return false.
-    //
-    // If it is a keyword, like "if", return true with the keyword's TokenKind
-    // in *ttp.
-    MOZ_MUST_USE bool checkForKeyword(JSAtom* atom, TokenKind* ttp);
-
-    // Same semantics as above, but for the provided keyword.
-    MOZ_MUST_USE bool checkForKeyword(const KeywordInfo* kw, TokenKind* ttp);
-
     // This class maps a userbuf offset (which is 0-indexed) to a line number
     // (which is 1-indexed) and a column index (which is 0-indexed).
     class SourceCoords
     {
         // For a given buffer holding source code, |lineStartOffsets_| has one
         // element per line of source code, plus one sentinel element.  Each
         // non-sentinel element holds the buffer offset for the start of the
         // corresponding line of source code.  For this example script:
@@ -1097,35 +1037,16 @@ class MOZ_STACK_CLASS TokenStream
     UniqueTwoByteChars  sourceMapURL_;      // source map's filename or null
     CharBuffer          tokenbuf;           // current token string buffer
     uint8_t             isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
     JSContext* const    cx;
     bool                mutedErrors;
     StrictModeGetter*   strictModeGetter;  // used to test for strict mode
 };
 
-class MOZ_STACK_CLASS AutoAwaitIsKeyword
-{
-private:
-    TokenStream* ts_;
-    bool oldAwaitIsKeyword_;
-
-public:
-    AutoAwaitIsKeyword(TokenStream* ts, bool awaitIsKeyword) {
-        ts_ = ts;
-        oldAwaitIsKeyword_ = ts_->awaitIsKeyword;
-        ts_->awaitIsKeyword = awaitIsKeyword;
-    }
-
-    ~AutoAwaitIsKeyword() {
-        ts_->awaitIsKeyword = oldAwaitIsKeyword_;
-        ts_ = nullptr;
-    }
-};
-
 extern const char*
 TokenKindToDesc(TokenKind tt);
 
 } // namespace frontend
 } // namespace js
 
 extern JS_FRIEND_API(int)
 js_fgets(char* buf, int size, FILE* file);
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -398,19 +398,17 @@ assertThrowsInstanceOf(function() {
 assertThrowsInstanceOf(function() {
     parseAsModule("export {} from");
 }, SyntaxError);
 
 assertThrowsInstanceOf(function() {
     parseAsModule("export {,} from 'a'");
 }, SyntaxError);
 
-assertThrowsInstanceOf(function() {
-    parseAsModule("export { true as a } from 'b'");
-}, SyntaxError);
+parseAsModule("export { true as a } from 'b'");
 
 assertThrowsInstanceOf(function () {
     parseAsModule("export { a } from 'b' f();");
 }, SyntaxError);
 
 assertThrowsInstanceOf(function () {
     parseAsModule("export *");
 }, SyntaxError);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -259,16 +259,17 @@ MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 
 MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR,  0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
 MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL,  0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
 MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
 MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
+MSG_DEF(JSMSG_INVALID_ID,              1, JSEXN_SYNTAXERR, "{0} is an invalid identifier")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LET_COMP_BINDING,        0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LEXICAL_DECL_LABEL,      1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
 MSG_DEF(JSMSG_GENERATOR_LABEL,         0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
 MSG_DEF(JSMSG_FUNCTION_LABEL,          0, JSEXN_SYNTAXERR, "functions cannot be labelled")
 MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL,   0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -49,51 +49,19 @@ js::AtomToPrintableString(JSContext* cx,
 JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
 #undef DEFINE_PROTO_STRING
 
 #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
 #undef CONST_CHAR_STR
 
 /* Constant strings that are not atomized. */
-const char js_break_str[]           = "break";
-const char js_case_str[]            = "case";
-const char js_catch_str[]           = "catch";
-const char js_class_str[]           = "class";
-const char js_const_str[]           = "const";
-const char js_continue_str[]        = "continue";
-const char js_debugger_str[]        = "debugger";
-const char js_default_str[]         = "default";
-const char js_do_str[]              = "do";
-const char js_else_str[]            = "else";
-const char js_enum_str[]            = "enum";
-const char js_export_str[]          = "export";
-const char js_extends_str[]         = "extends";
-const char js_finally_str[]         = "finally";
-const char js_for_str[]             = "for";
 const char js_getter_str[]          = "getter";
-const char js_if_str[]              = "if";
-const char js_implements_str[]      = "implements";
-const char js_import_str[]          = "import";
-const char js_in_str[]              = "in";
-const char js_instanceof_str[]      = "instanceof";
-const char js_interface_str[]       = "interface";
-const char js_package_str[]         = "package";
-const char js_private_str[]         = "private";
-const char js_protected_str[]       = "protected";
-const char js_public_str[]          = "public";
 const char js_send_str[]            = "send";
 const char js_setter_str[]          = "setter";
-const char js_switch_str[]          = "switch";
-const char js_this_str[]            = "this";
-const char js_try_str[]             = "try";
-const char js_typeof_str[]          = "typeof";
-const char js_void_str[]            = "void";
-const char js_while_str[]           = "while";
-const char js_with_str[]            = "with";
 
 // Use a low initial capacity for atom hash tables to avoid penalizing runtimes
 // which create a small number of atoms.
 static const uint32_t JS_STRING_HASH_COUNT = 64;
 
 AtomSet::Ptr js::FrozenAtomSet::readonlyThreadsafeLookup(const AtomSet::Lookup& l) const {
     return mSet->readonlyThreadsafeLookup(l);
 }
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -145,54 +145,19 @@ AtomIsPinnedInRuntime(JSRuntime* rt, JSA
 JS_FOR_EACH_PROTOTYPE(DECLARE_PROTO_STR)
 #undef DECLARE_PROTO_STR
 
 #define DECLARE_CONST_CHAR_STR(idpart, id, text)  extern const char js_##idpart##_str[];
 FOR_EACH_COMMON_PROPERTYNAME(DECLARE_CONST_CHAR_STR)
 #undef DECLARE_CONST_CHAR_STR
 
 /* Constant strings that are not atomized. */
-extern const char js_break_str[];
-extern const char js_case_str[];
-extern const char js_catch_str[];
-extern const char js_class_str[];
-extern const char js_close_str[];
-extern const char js_const_str[];
-extern const char js_continue_str[];
-extern const char js_debugger_str[];
-extern const char js_default_str[];
-extern const char js_do_str[];
-extern const char js_else_str[];
-extern const char js_enum_str[];
-extern const char js_export_str[];
-extern const char js_extends_str[];
-extern const char js_finally_str[];
-extern const char js_for_str[];
 extern const char js_getter_str[];
-extern const char js_if_str[];
-extern const char js_implements_str[];
-extern const char js_import_str[];
-extern const char js_in_str[];
-extern const char js_instanceof_str[];
-extern const char js_interface_str[];
-extern const char js_package_str[];
-extern const char js_private_str[];
-extern const char js_protected_str[];
-extern const char js_public_str[];
 extern const char js_send_str[];
 extern const char js_setter_str[];
-extern const char js_static_str[];
-extern const char js_super_str[];
-extern const char js_switch_str[];
-extern const char js_this_str[];
-extern const char js_try_str[];
-extern const char js_typeof_str[];
-extern const char js_void_str[];
-extern const char js_while_str[];
-extern const char js_with_str[];
 
 namespace js {
 
 class AutoLockForExclusiveAccess;
 
 /*
  * Atom tracing and garbage collection hooks.
  */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -627,21 +627,21 @@ if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
     ]
     if CONFIG['LINUX_HEADERS_INCLUDES']:
         SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
 else:
     SOURCES += [
         'perf/pm_stub.cpp'
     ]
 
-GENERATED_FILES += ['jsautokw.h']
-jsautokw = GENERATED_FILES['jsautokw.h']
-jsautokw.script = 'jsautokw.py'
-jsautokw.inputs += [
-    'vm/Keywords.h'
+GENERATED_FILES += ['frontend/ReservedWordsGenerated.h']
+ReservedWordsGenerated = GENERATED_FILES['frontend/ReservedWordsGenerated.h']
+ReservedWordsGenerated.script = 'frontend/GenerateReservedWords.py'
+ReservedWordsGenerated.inputs += [
+    'frontend/ReservedWords.h'
 ]
 
 # JavaScript must be built shared, even for static builds, as it is used by
 # other modules which are always built shared. Failure to do so results in
 # the js code getting copied into xpinstall and jsd as well as mozilla-bin,
 # and then the static data cells used for locking no longer work.
 #
 # In fact, we now build both a static and a shared library, as the
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -34,95 +34,108 @@
     macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
     macro(async, async, "async") \
     macro(await, await, "await") \
     macro(Bool8x16, Bool8x16, "Bool8x16") \
     macro(Bool16x8, Bool16x8, "Bool16x8") \
     macro(Bool32x4, Bool32x4, "Bool32x4") \
     macro(Bool64x2, Bool64x2, "Bool64x2") \
     macro(boundWithSpace, boundWithSpace, "bound ") \
+    macro(break, break_, "break") \
     macro(breakdown, breakdown, "breakdown") \
     macro(buffer, buffer, "buffer") \
     macro(builder, builder, "builder") \
     macro(by, by, "by") \
     macro(byteAlignment, byteAlignment, "byteAlignment") \
     macro(byteLength, byteLength, "byteLength") \
     macro(byteOffset, byteOffset, "byteOffset") \
     macro(bytes, bytes, "bytes") \
     macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \
     macro(call, call, "call") \
     macro(callContentFunction, callContentFunction, "callContentFunction") \
     macro(callee, callee, "callee") \
     macro(caller, caller, "caller") \
     macro(callFunction, callFunction, "callFunction") \
+    macro(case, case_, "case") \
     macro(caseFirst, caseFirst, "caseFirst") \
-    macro(class_, class_, "class") \
+    macro(catch, catch_, "catch") \
+    macro(class, class_, "class") \
     macro(close, close, "close") \
     macro(Collator, Collator, "Collator") \
     macro(collections, collections, "collections") \
     macro(columnNumber, columnNumber, "columnNumber") \
     macro(comma, comma, ",") \
     macro(compare, compare, "compare") \
     macro(configurable, configurable, "configurable") \
+    macro(const, const_, "const") \
     macro(construct, construct, "construct") \
     macro(constructContentFunction, constructContentFunction, "constructContentFunction") \
     macro(constructor, constructor, "constructor") \
+    macro(continue, continue_, "continue") \
     macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \
     macro(copyWithin, copyWithin, "copyWithin") \
     macro(count, count, "count") \
     macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \
     macro(currency, currency, "currency") \
     macro(currencyDisplay, currencyDisplay, "currencyDisplay") \
     macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
     macro(day, day, "day") \
     macro(dayPeriod, dayPeriod, "dayPeriod") \
+    macro(debugger, debugger, "debugger") \
     macro(decimal, decimal, "decimal") \
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
     macro(DefaultBaseClassConstructor, DefaultBaseClassConstructor, "DefaultBaseClassConstructor") \
     macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \
-    macro(default_, default_, "default") \
+    macro(default, default_, "default") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(displayURL, displayURL, "displayURL") \
+    macro(do, do_, "do") \
     macro(done, done, "done") \
     macro(dotGenerator, dotGenerator, ".generator") \
     macro(dotThis, dotThis, ".this") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
+    macro(else, else_, "else") \
     macro(empty, empty, "") \
     macro(emptyRegExp, emptyRegExp, "(?:)") \
     macro(encodeURI, encodeURI, "encodeURI") \
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(endTimestamp, endTimestamp, "endTimestamp") \
     macro(entries, entries, "entries") \
+    macro(enum, enum_, "enum") \
     macro(enumerable, enumerable, "enumerable") \
     macro(enumerate, enumerate, "enumerate") \
     macro(era, era, "era") \
     macro(ErrorToStringWithTrailingNewline, ErrorToStringWithTrailingNewline, "ErrorToStringWithTrailingNewline") \
     macro(escape, escape, "escape") \
     macro(eval, eval, "eval") \
     macro(exec, exec, "exec") \
+    macro(export, export_, "export") \
+    macro(extends, extends, "extends") \
     macro(false, false_, "false") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
     macro(fill, fill, "fill") \
+    macro(finally, finally_, "finally") \
     macro(find, find, "find") \
     macro(findIndex, findIndex, "findIndex") \
     macro(firstDayOfWeek, firstDayOfWeek, "firstDayOfWeek") \
     macro(fix, fix, "fix") \
     macro(flags, flags, "flags") \
     macro(float32, float32, "float32") \
     macro(Float32x4, Float32x4, "Float32x4") \
     macro(float64, float64, "float64") \
     macro(Float64x2, Float64x2, "Float64x2") \
+    macro(for, for_, "for") \
     macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
     macro(forEach, forEach, "forEach") \
     macro(format, format, "format") \
     macro(formatToParts, formatToParts, "formatToParts") \
     macro(fraction, fraction, "fraction") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(fulfilled, fulfilled, "fulfilled") \
@@ -141,37 +154,43 @@
     macro(getPrototypeOf, getPrototypeOf, "getPrototypeOf") \
     macro(global, global, "global") \
     macro(group, group, "group") \
     macro(Handle, Handle, "Handle") \
     macro(has, has, "has") \
     macro(hasOwn, hasOwn, "hasOwn") \
     macro(hasOwnProperty, hasOwnProperty, "hasOwnProperty") \
     macro(hour, hour, "hour") \
+    macro(if, if_, "if") \
     macro(ignoreCase, ignoreCase, "ignoreCase") \
     macro(ignorePunctuation, ignorePunctuation, "ignorePunctuation") \
+    macro(implements, implements, "implements") \
+    macro(import, import, "import") \
+    macro(in, in, "in") \
     macro(includes, includes, "includes") \
     macro(incumbentGlobal, incumbentGlobal, "incumbentGlobal") \
     macro(index, index, "index") \
     macro(infinity, infinity, "infinity") \
     macro(Infinity, Infinity, "Infinity") \
     macro(InitializeCollator, InitializeCollator, "InitializeCollator") \
     macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
     macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
     macro(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
     macro(innermost, innermost, "innermost") \
     macro(inNursery, inNursery, "inNursery") \
     macro(input, input, "input") \
+    macro(instanceof, instanceof, "instanceof") \
     macro(int8, int8, "int8") \
     macro(int16, int16, "int16") \
     macro(int32, int32, "int32") \
     macro(Int8x16, Int8x16, "Int8x16") \
     macro(Int16x8, Int16x8, "Int16x8") \
     macro(Int32x4, Int32x4, "Int32x4") \
     macro(integer, integer, "integer") \
+    macro(interface, interface, "interface") \
     macro(InterpretGeneratorResume, InterpretGeneratorResume, "InterpretGeneratorResume") \
     macro(isEntryPoint, isEntryPoint, "isEntryPoint") \
     macro(isExtensible, isExtensible, "isExtensible") \
     macro(isFinite, isFinite, "isFinite") \
     macro(isNaN, isNaN, "isNaN") \
     macro(isPrototypeOf, isPrototypeOf, "isPrototypeOf") \
     macro(IterableToList, IterableToList, "IterableToList") \
     macro(iterate, iterate, "iterate") \
@@ -239,27 +258,31 @@
     macro(objectUndefined, objectUndefined, "[object Undefined]") \
     macro(of, of, "of") \
     macro(offset, offset, "offset") \
     macro(optimizedOut, optimizedOut, "optimizedOut") \
     macro(other, other, "other") \
     macro(outOfMemory, outOfMemory, "out of memory") \
     macro(ownKeys, ownKeys, "ownKeys") \
     macro(Object_valueOf, Object_valueOf, "Object_valueOf") \
+    macro(package, package, "package") \
     macro(parseFloat, parseFloat, "parseFloat") \
     macro(parseInt, parseInt, "parseInt") \
     macro(pattern, pattern, "pattern") \
     macro(pending, pending, "pending") \
     macro(PluralRules, PluralRules, "PluralRules") \
     macro(PluralRulesSelect, PluralRulesSelect, "Intl_PluralRules_Select") \
     macro(percentSign, percentSign, "percentSign") \
     macro(plusSign, plusSign, "plusSign") \
+    macro(public, public_, "public") \
     macro(preventExtensions, preventExtensions, "preventExtensions") \
+    macro(private, private_, "private") \
     macro(promise, promise, "promise") \
     macro(propertyIsEnumerable, propertyIsEnumerable, "propertyIsEnumerable") \
+    macro(protected, protected_, "protected") \
     macro(proto, proto, "__proto__") \
     macro(prototype, prototype, "prototype") \
     macro(proxy, proxy, "proxy") \
     macro(raw, raw, "raw") \
     macro(reason, reason, "reason") \
     macro(RegExpBuiltinExec, RegExpBuiltinExec, "RegExpBuiltinExec") \
     macro(RegExpFlagsGetter, RegExpFlagsGetter, "RegExpFlagsGetter") \
     macro(RegExpMatcher, RegExpMatcher, "RegExpMatcher") \
@@ -298,33 +321,37 @@
     macro(static, static_, "static") \
     macro(std_Function_apply, std_Function_apply, "std_Function_apply") \
     macro(sticky, sticky, "sticky") \
     macro(StringIterator, StringIterator, "String Iterator") \
     macro(strings, strings, "strings") \
     macro(StructType, StructType, "StructType") \
     macro(style, style, "style") \
     macro(super, super, "super") \
+    macro(switch, switch_, "switch") \
     macro(Symbol_iterator_fun, Symbol_iterator_fun, "[Symbol.iterator]") \
     macro(target, target, "target") \
     macro(test, test, "test") \
     macro(then, then, "then") \
+    macro(this, this_, "this") \
     macro(throw, throw_, "throw") \
     macro(timestamp, timestamp, "timestamp") \
     macro(timeZone, timeZone, "timeZone") \
     macro(timeZoneName, timeZoneName, "timeZoneName") \
     macro(toGMTString, toGMTString, "toGMTString") \
     macro(toISOString, toISOString, "toISOString") \
     macro(toJSON, toJSON, "toJSON") \
     macro(toLocaleString, toLocaleString, "toLocaleString") \
     macro(toSource, toSource, "toSource") \
     macro(toString, toString, "toString") \
     macro(toUTCString, toUTCString, "toUTCString") \
     macro(true, true_, "true") \
+    macro(try, try_, "try") \
     macro(type, type, "type") \
+    macro(typeof, typeof_, "typeof") \
     macro(uint8, uint8, "uint8") \
     macro(uint8Clamped, uint8Clamped, "uint8Clamped") \
     macro(uint16, uint16, "uint16") \
     macro(uint32, uint32, "uint32") \
     macro(Uint8x16, Uint8x16, "Uint8x16") \
     macro(Uint16x8, Uint16x8, "Uint16x8") \
     macro(Uint32x4, Uint32x4, "Uint32x4") \
     macro(unescape, unescape, "unescape") \
@@ -334,31 +361,34 @@
     macro(unsized, unsized, "unsized") \
     macro(unwatch, unwatch, "unwatch") \
     macro(UnwrapAndCallRegExpBuiltinExec, UnwrapAndCallRegExpBuiltinExec, "UnwrapAndCallRegExpBuiltinExec") \
     macro(url, url, "url") \
     macro(usage, usage, "usage") \
     macro(useAsm, useAsm, "use asm") \
     macro(useGrouping, useGrouping, "useGrouping") \
     macro(useStrict, useStrict, "use strict") \
+    macro(void, void_, "void") \
     macro(value, value, "value") \
     macro(valueOf, valueOf, "valueOf") \
     macro(values, values, "values") \
     macro(var, var, "var") \
     macro(variable, variable, "variable") \
     macro(void0, void0, "(void 0)") \
     macro(wasm, wasm, "wasm") \
     macro(wasmcall, wasmcall, "wasmcall") \
     macro(watch, watch, "watch") \
     macro(WeakMapConstructorInit, WeakMapConstructorInit, "WeakMapConstructorInit") \
     macro(WeakSetConstructorInit, WeakSetConstructorInit, "WeakSetConstructorInit") \
     macro(WeakSet_add, WeakSet_add, "WeakSet_add") \
     macro(weekday, weekday, "weekday") \
     macro(weekendEnd, weekendEnd, "weekendEnd") \
     macro(weekendStart, weekendStart, "weekendStart") \
+    macro(while, while_, "while") \
+    macro(with, with, "with") \
     macro(writable, writable, "writable") \
     macro(year, year, "year") \
     macro(yield, yield, "yield") \
     /* Type names must be contiguous and ordered; see js::TypeName. */ \
     macro(undefined, undefined, "undefined") \
     macro(object, object, "object") \
     macro(function, function, "function") \
     macro(string, string, "string") \
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -7038,17 +7038,17 @@ ParseFunction(ModuleValidator& m, ParseN
     TokenStream& tokenStream = m.tokenStream();
 
     tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
     *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
 
     TokenKind tk;
     if (!tokenStream.getToken(&tk, TokenStream::Operand))
         return false;
-    if (tk != TOK_NAME && tk != TOK_YIELD)
+    if (!TokenKindIsPossibleIdentifier(tk))
         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
 
     RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
     if (!name)
         return false;
 
     ParseNode* fn = m.parser().handler.newFunctionStatement();
     if (!fn)