summaryrefslogtreecommitdiffstats
path: root/js/src/frontend
diff options
context:
space:
mode:
authorGaming4JC <g4jc@hyperbola.info>2019-06-09 16:14:01 -0400
committerGaming4JC <g4jc@hyperbola.info>2019-07-18 22:38:35 -0400
commit05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6 (patch)
tree036823d2032034267a87f28139fb3c47d79d6a65 /js/src/frontend
parent7fb92548e6aafa512701ad0a8bb5156251d40457 (diff)
downloadUXP-05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6.tar
UXP-05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6.tar.gz
UXP-05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6.tar.lz
UXP-05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6.tar.xz
UXP-05c9c752a34ce4c9d246b63e2fbb46eaa946f8b6.zip
1336783 - Part 1: Rework on reserved word and remove TokenStream::KeywordIsName.
Diffstat (limited to 'js/src/frontend')
-rw-r--r--js/src/frontend/GenerateReservedWords.py213
-rw-r--r--js/src/frontend/Parser.cpp716
-rw-r--r--js/src/frontend/Parser.h48
-rw-r--r--js/src/frontend/ReservedWords.h81
-rw-r--r--js/src/frontend/TokenKind.h99
-rw-r--r--js/src/frontend/TokenStream.cpp191
-rw-r--r--js/src/frontend/TokenStream.h135
7 files changed, 858 insertions, 625 deletions
diff --git a/js/src/frontend/GenerateReservedWords.py b/js/src/frontend/GenerateReservedWords.py
new file mode 100644
index 000000000..bd698cc5f
--- /dev/null
+++ b/js/src/frontend/GenerateReservedWords.py
@@ -0,0 +1,213 @@
+# 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_reserved_word_list(filename):
+ macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$")
+
+ reserved_word_list = []
+ index = 0
+ with open(filename, 'r') as f:
+ for line in f:
+ m = macro_pat.search(line)
+ if m:
+ reserved_word_list.append((index, m.group(1)))
+ index += 1
+
+ assert(len(reserved_word_list) != 0)
+
+ 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(reserved_word_list, column):
+ assert(len(reserved_word_list) != 0);
+
+ chars_dict = {}
+ 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, 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(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:
+ min_span = span
+ min_span_index = index
+
+ if index == 0 or min_count > count:
+ 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(reserved_word_list, column):
+ assert(len(reserved_word_list) != 0);
+
+ column_dict = {}
+ 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, word): ord(char))
+
+def generate_letter_switch(opt, unprocessed_columns, reserved_word_list,
+ columns=None):
+ assert(len(reserved_word_list) != 0);
+
+ if not columns:
+ columns = range(0, unprocessed_columns)
+
+ if len(reserved_word_list) == 1:
+ index, word = reserved_word_list[0]
+
+ if unprocessed_columns == 0:
+ line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
+ return
+
+ if unprocessed_columns > opt['char_tail_test_threshold']:
+ line(opt, 'JSRW_TEST_GUESS({}) /* {} */'.format(index, word))
+ return
+
+ conds = []
+ for column in columns[0:unprocessed_columns]:
+ quoted = repr(word[column])
+ conds.append('JSRW_AT({})=={}'.format(column, quoted))
+
+ line(opt, 'if ({}) {{'.format(' && '.join(conds)))
+
+ indent(opt)
+ line(opt, 'JSRW_GOT_MATCH({}) /* {} */'.format(index, word))
+ dedent(opt)
+
+ line(opt, '}')
+ line(opt, 'JSRW_NO_MATCH()')
+ return
+
+ assert(unprocessed_columns != 0);
+
+ 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(reserved_word_list, optimal_column)
+
+ if not use_if:
+ line(opt, 'switch (JSRW_AT({})) {{'.format(optimal_column))
+
+ for char, reserved_word_list_per_column in list_per_column:
+ quoted = repr(char)
+ if use_if:
+ line(opt, 'if (JSRW_AT({}) == {}) {{'.format(optimal_column,
+ quoted))
+ else:
+ line(opt, ' case {}:'.format(quoted))
+
+ indent(opt)
+ generate_letter_switch(opt, unprocessed_columns - 1,
+ reserved_word_list_per_column, columns)
+ dedent(opt)
+
+ if use_if:
+ line(opt, '}')
+
+ if not use_if:
+ line(opt, '}')
+
+ line(opt, 'JSRW_NO_MATCH()')
+
+def split_list_per_length(reserved_word_list):
+ assert(len(reserved_word_list) != 0);
+
+ length_dict = {}
+ 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, word): length)
+
+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(reserved_word_list)))
+ for index, word in reserved_word_list:
+ line(opt, ' * {}'.format(word))
+ line(opt, ' */')
+
+ 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 (JSRW_LENGTH()) {')
+
+ for length, reserved_word_list_per_length in list_per_length:
+ if use_if:
+ line(opt, 'if (JSRW_LENGTH() == {}) {{'.format(length))
+ else:
+ line(opt, ' case {}:'.format(length))
+
+ indent(opt)
+ 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, 'JSRW_NO_MATCH()')
+
+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, reserved_word_list)
+
+if __name__ == '__main__':
+ main(sys.stdout, *sys.argv[1:])
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 923b42870..9b057fb72 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -62,19 +62,27 @@ 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
@@ -753,7 +761,8 @@ ParserBase::ParserBase(ExclusiveContext* cx, LifoAlloc& alloc,
checkOptionsCalled(false),
#endif
abortedSyntaxParse(false),
- isUnexpectedEOF_(false)
+ isUnexpectedEOF_(false),
+ awaitIsKeyword_(false)
{
cx->perThreadData->frontendCollectionPool.addActiveCompilation();
tempPoolMark = alloc.mark();
@@ -810,6 +819,22 @@ 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)
@@ -936,7 +961,7 @@ Parser<ParseHandler>::parse()
/*
* 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)
@@ -945,7 +970,8 @@ ParserBase::isValidStrictBinding(PropertyName* name)
name != context->names().arguments &&
name != context->names().let &&
name != context->names().static_ &&
- !(IsKeyword(name) && name != context->names().await);
+ name != context->names().yield &&
+ !IsStrictReservedWord(name);
}
/*
@@ -959,13 +985,30 @@ 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;
}
@@ -2094,7 +2137,7 @@ Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
if (!mn)
return null();
- AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, true);
+ AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, true);
ParseNode* pn = statementList(YieldIsKeyword);
if (!pn)
return null();
@@ -2417,7 +2460,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
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))
{
@@ -2719,10 +2762,7 @@ Parser<ParseHandler>::matchOrInsertSemicolonHelper(TokenStream::Modifier modifie
* 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;
}
@@ -2845,7 +2885,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
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;
}
@@ -2901,7 +2941,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
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)) {
@@ -2922,7 +2962,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
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;
}
@@ -2952,20 +2992,15 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
- case TOK_NAME:
- case TOK_YIELD: {
- 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");
+ default: {
+ if (!TokenKindIsPossibleIdentifier(tt)) {
+ error(JSMSG_MISSING_FORMAL);
return false;
}
+ if (parenFreeArrow)
+ funbox->setStart(tokenStream);
+
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
@@ -2980,10 +3015,6 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
break;
}
-
- default:
- error(JSMSG_MISSING_FORMAL);
- return false;
}
if (positionalFormals.length() >= ARGNO_LIMIT) {
@@ -3507,7 +3538,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
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;
@@ -3656,7 +3687,7 @@ Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHan
}
RootedPropertyName name(context);
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -3712,7 +3743,7 @@ Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invo
{
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))
@@ -3731,7 +3762,7 @@ Parser<ParseHandler>::functionExpr(uint32_t preludeStart, InvokedPrediction invo
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();
@@ -3768,18 +3799,6 @@ IsEscapeFreeStringLiteral(const TokenPos& pos, JSAtom* str)
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)
@@ -3998,7 +4017,7 @@ Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<Prop
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));
@@ -4551,7 +4570,8 @@ Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, Token
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();
}
@@ -4721,16 +4741,13 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// 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;
}
@@ -4738,23 +4755,16 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
Rooted<PropertyName*> importName(context, tokenStream.currentName());
TokenPos importNamePos = pos();
- TokenKind maybeAs;
- if (!tokenStream.peekToken(&maybeAs))
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_AS))
return null();
- if (maybeAs == TOK_NAME &&
- tokenStream.nextName() == context->names().as)
- {
- tokenStream.consumeKnownToken(TOK_NAME);
-
- if (!checkUnescapedName())
- return false;
-
+ 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;
}
@@ -4764,10 +4774,7 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
// 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;
}
}
@@ -4806,18 +4813,10 @@ Parser<FullParseHandler>::namedImportsOrNamespaceImport(TokenKind tt, Node impor
}
} 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;
- }
+ MUST_MATCH_TOKEN(TOK_AS, JSMSG_AS_AFTER_IMPORT_STAR);
- if (!checkUnescapedName())
- return false;
-
- MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+ MUST_MATCH_TOKEN_FUNC(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME);
Node importName = newName(context->names().star);
if (!importName)
@@ -4878,8 +4877,15 @@ Parser<FullParseHandler>::importDeclaration()
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
@@ -4922,29 +4928,19 @@ Parser<FullParseHandler>::importDeclaration()
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();
@@ -5050,7 +5046,7 @@ Parser<FullParseHandler>::exportDeclaration()
if (tt == TOK_RC)
break;
- if (tt != TOK_NAME) {
+ if (!TokenKindIsPossibleIdentifierName(tt)) {
error(JSMSG_NO_BINDING_NAME);
return null();
}
@@ -5060,10 +5056,10 @@ Parser<FullParseHandler>::exportDeclaration()
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)
@@ -5098,21 +5094,18 @@ Parser<FullParseHandler>::exportDeclaration()
// 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))
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_FROM, TokenStream::Operand))
return null();
- if (tt == TOK_NAME &&
- tokenStream.currentToken().name() == context->names().from &&
- !tokenStream.currentToken().nameContainsEscape())
- {
+ if (matched) {
MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
Node moduleSpec = stringLiteral();
@@ -5129,8 +5122,6 @@ Parser<FullParseHandler>::exportDeclaration()
return node;
}
- tokenStream.ungetToken();
-
if (!matchOrInsertSemicolonAfterNonExpression())
return null();
break;
@@ -5151,14 +5142,11 @@ Parser<FullParseHandler>::exportDeclaration()
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();
@@ -5227,10 +5215,7 @@ Parser<FullParseHandler>::exportDeclaration()
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();
@@ -5276,19 +5261,13 @@ Parser<FullParseHandler>::exportDeclaration()
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);
@@ -5338,10 +5317,7 @@ Parser<ParseHandler>::consequentOrAlternative(YieldHandling yieldHandling)
//
// 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
@@ -5514,13 +5490,9 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
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;
@@ -5570,15 +5542,12 @@ Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
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))
@@ -5699,7 +5668,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
if (allowsForEachIn()) {
bool matched;
- if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+ if (!tokenStream.matchToken(&matched, TOK_EACH))
return null();
if (matched) {
iflags = JSITER_FOREACH;
@@ -6410,12 +6379,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
* 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.
@@ -6490,8 +6459,12 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
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();
@@ -6502,10 +6475,6 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
return null();
break;
}
-
- default:
- error(JSMSG_CATCH_IDENTIFIER);
- return null();
}
Node catchGuard = null();
@@ -6664,7 +6633,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
RootedPropertyName name(context);
- if (tt == TOK_NAME || tt == TOK_YIELD) {
+ if (TokenKindIsPossibleIdentifier(tt)) {
name = bindingIdentifier(yieldHandling);
if (!name)
return null();
@@ -6721,7 +6690,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
bool seenConstructor = false;
for (;;) {
TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_RC)
break;
@@ -6730,22 +6699,18 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
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();
-
isStatic = true;
} else {
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
tokenStream.ungetToken();
}
} else {
@@ -6865,9 +6830,7 @@ 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;
@@ -6879,18 +6842,19 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
if (next == TOK_LB || next == TOK_LC)
return true;
- // 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;
- }
+ // 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;
- // One non-"yield" TOK_NAME edge case deserves special comment.
- // Consider this:
+ // Otherwise a let declaration must have a name.
+ if (TokenKindIsPossibleIdentifier(next)) {
+ // A "let" edge case deserves special comment. Consider this:
//
// let // not an ASI opportunity
// let;
@@ -6903,16 +6867,6 @@ Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHand
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;
}
@@ -6975,16 +6929,21 @@ Parser<ParseHandler>::statement(YieldHandling 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) {
@@ -6994,7 +6953,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
} 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.
@@ -7005,7 +6964,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
if (!tokenStream.peekTokenSameLine(&nextSameLine))
return null();
- MOZ_ASSERT(nextSameLine == TOK_NAME ||
+ MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) ||
nextSameLine == TOK_LC ||
nextSameLine == TOK_EOL);
@@ -7029,9 +6988,6 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
case TOK_NEW:
return expressionStatement(yieldHandling, PredictInvoked);
- default:
- return expressionStatement(yieldHandling);
-
// IfStatement[?Yield, ?Return]
case TOK_IF:
return ifStatement(yieldHandling);
@@ -7077,7 +7033,7 @@ Parser<ParseHandler>::statement(YieldHandling yieldHandling)
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:
@@ -7183,28 +7139,29 @@ Parser<ParseHandler>::statementListItem(YieldHandling 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 (tt == TOK_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) {
- uint32_t preludeStart = pos().begin;
- tokenStream.consumeKnownToken(TOK_FUNCTION);
- return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction);
- }
+ if (tt == TOK_ASYNC) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+ if (nextSameLine == TOK_FUNCTION) {
+ uint32_t preludeStart = pos().begin;
+ tokenStream.consumeKnownToken(TOK_FUNCTION);
+ return functionStmt(preludeStart, yieldHandling, NameRequired, AsyncFunction);
}
}
@@ -7217,9 +7174,6 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
case TOK_NEW:
return expressionStatement(yieldHandling, PredictInvoked);
- default:
- return expressionStatement(yieldHandling);
-
// IfStatement[?Yield, ?Return]
case TOK_IF:
return ifStatement(yieldHandling);
@@ -7265,7 +7219,7 @@ Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
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:
@@ -7631,6 +7585,9 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
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();
@@ -7661,15 +7618,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
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;
}
@@ -7683,14 +7637,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
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));
@@ -7759,24 +7711,18 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
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();
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
- if (nextSameLine == TOK_ARROW) {
- tokenStream.ungetToken();
- } else {
- generatorKind = StarGenerator;
- asyncKind = AsyncFunction;
- }
- } else {
+ if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
+ } else {
+ generatorKind = StarGenerator;
+ asyncKind = AsyncFunction;
}
}
@@ -8025,20 +7971,17 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
}
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();
+ if (pc->isAsync()) {
+ Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+ if (!kid)
+ return null();
+ pc->lastAwaitOffset = begin;
+ return newAwaitExpression(begin, kid);
}
-
- 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);
@@ -8176,7 +8119,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
// 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);
@@ -8187,7 +8130,7 @@ Parser<ParseHandler>::comprehensionFor(GeneratorKind comprehensionKind)
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);
@@ -8522,9 +8465,9 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
Node nextMember;
if (tt == TOK_DOT) {
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
- if (tt == TOK_NAME) {
+ if (TokenKindIsPossibleIdentifierName(tt)) {
PropertyName* field = tokenStream.currentName();
if (handler.isSuperBase(lhs) && !checkAndMarkSuperScope()) {
error(JSMSG_BAD_SUPERPROP, "property");
@@ -8695,46 +8638,52 @@ Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
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;
}
}
@@ -8744,52 +8693,34 @@ Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling,
template <typename ParseHandler>
PropertyName*
-Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+Parser<ParseHandler>::labelOrIdentifierReference(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);
+ // 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.
- ident = context->names().yield;
- isYield = true;
- }
+ return checkLabelOrIdentifierReference(tokenStream.currentName(), pos().begin, yieldHandling);
+}
- 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;
- }
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+{
+ PropertyName* ident = labelOrIdentifierReference(yieldHandling);
+ if (!ident)
+ return nullptr;
- badName = ident == context->names().let
- ? "let"
- : ident == context->names().static_
- ? "static"
- : nullptr;
- if (badName) {
- error(JSMSG_RESERVED_ID, badName);
- return nullptr;
- }
+ if (pc->sc()->strict()) {
+ if (ident == context->names().arguments) {
+ error(JSMSG_BAD_STRICT_ASSIGN, "arguments");
+ return nullptr;
}
- } else {
- if (yieldHandling == YieldIsKeyword ||
- pc->sc()->strict() ||
- versionNumber() >= JSVERSION_1_7)
- {
- error(JSMSG_RESERVED_ID, "yield");
+
+ if (ident == context->names().eval) {
+ error(JSMSG_BAD_STRICT_ASSIGN, "eval");
return nullptr;
}
}
@@ -8970,7 +8901,7 @@ 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");
@@ -8979,14 +8910,11 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
bool isAsync = false;
if (ltok == TOK_MUL) {
isGenerator = true;
- if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&ltok))
return null();
}
- if (ltok == TOK_NAME &&
- tokenStream.currentName() == context->names().async &&
- !tokenStream.currentToken().nameContainsEscape())
- {
+ if (ltok == TOK_ASYNC) {
// AsyncMethod[Yield, Await]:
// async [no LineTerminator here] PropertyName[?Yield, ?Await] ...
//
@@ -9002,16 +8930,13 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
// ComputedPropertyName[Yield, Await]:
// [ ...
TokenKind tt = TOK_EOF;
- if (!tokenStream.peekTokenSameLine(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
- if (tt == TOK_STRING || tt == TOK_NUMBER || tt == TOK_LB ||
- tt == TOK_NAME || tt == TOK_YIELD)
- {
+ if (tt != TOK_LP && tt != TOK_COLON && tt != TOK_RC && tt != TOK_ASSIGN) {
isAsync = true;
- tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
ltok = tt;
} else {
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
+ tokenStream.ungetToken();
}
}
@@ -9038,41 +8963,36 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
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))
+ if (!tokenStream.peekToken(&tt))
return null();
- if (tt == TOK_NAME) {
- if (!checkUnescapedName())
- return null();
-
- tokenStream.consumeKnownToken(TOK_NAME, TokenStream::KeywordIsName);
+ 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());
@@ -9086,10 +9006,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
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())
@@ -9097,10 +9014,7 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
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);
}
@@ -9109,7 +9023,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
propName = handler.newObjectLiteralPropertyName(propAtom.get(), pos());
if (!propName)
return null();
- tokenStream.addModifierException(TokenStream::NoneIsKeywordIsName);
break;
}
@@ -9127,10 +9040,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
return null();
break;
}
-
- default:
- error(JSMSG_BAD_PROP_ID);
- return null();
}
TokenKind tt;
@@ -9146,7 +9055,9 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling, Node propList,
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();
@@ -9214,7 +9125,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
RootedAtom propAtom(context);
for (;;) {
TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+ if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_RC)
break;
@@ -9274,17 +9185,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
* 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();
@@ -9299,17 +9200,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
* 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();
@@ -9482,14 +9373,11 @@ Parser<ParseHandler>::tryNewTarget(Node &newTarget)
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;
@@ -9572,11 +9460,13 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
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();
@@ -9648,7 +9538,7 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// 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();
}
@@ -9675,10 +9565,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
// Return an arbitrary expression node. See case TOK_RP above.
return handler.newNullLiteral(pos());
}
-
- default:
- error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt));
- return null();
}
}
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index 35b9384a8..9ec862d92 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -734,6 +734,9 @@ class UsedNameTracker
}
};
+template <typename ParseHandler>
+class AutoAwaitIsKeyword;
+
class ParserBase : public StrictModeGetter
{
private:
@@ -783,7 +786,13 @@ class ParserBase : public StrictModeGetter
/* 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(ExclusiveContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length, bool foldConstants,
UsedNameTracker& usedNames, Parser<SyntaxParseHandler>* syntaxParser,
@@ -998,6 +1007,9 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
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
@@ -1047,8 +1059,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
void trace(JSTracer* trc);
- bool checkUnescapedName();
-
private:
Parser* thisForCtor() { return this; }
@@ -1321,17 +1331,18 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
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() {
@@ -1455,6 +1466,25 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
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 */
diff --git a/js/src/frontend/ReservedWords.h b/js/src/frontend/ReservedWords.h
new file mode 100644
index 000000000..27f5b11c1
--- /dev/null
+++ b/js/src/frontend/ReservedWords.h
@@ -0,0 +1,81 @@
+/* -*- 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 reserved word tokens. */
+
+#ifndef vm_ReservedWords_h
+#define vm_ReservedWords_h
+
+#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) \
+ macro(delete, delete_, TOK_DELETE) \
+ macro(do, do_, TOK_DO) \
+ macro(else, else_, TOK_ELSE) \
+ macro(finally, finally_, TOK_FINALLY) \
+ macro(for, for_, TOK_FOR) \
+ macro(function, function, TOK_FUNCTION) \
+ macro(if, if_, TOK_IF) \
+ 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(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(class, class_, TOK_CLASS) \
+ macro(extends, extends, TOK_EXTENDS) \
+ macro(super, super, TOK_SUPER) \
+ \
+ /* 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 word in strict mode, but a keyword in JS1.7 even \
+ * when strict. Punt logic to parser. \
+ */ \
+ macro(yield, yield, TOK_YIELD)
+
+#endif /* vm_ReservedWords_h */
diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h
index 6f22d78e5..98f23fec8 100644
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -81,9 +81,12 @@
\
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'") \
@@ -106,16 +109,43 @@
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 \
@@ -149,7 +179,9 @@
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, "'<<'") \
@@ -168,7 +200,9 @@
\
/* 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, "'~'") \
\
@@ -239,6 +273,61 @@ 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
diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp
index 76ea7e821..b8623d545 100644
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -24,10 +24,10 @@
#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"
@@ -40,65 +40,65 @@ 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>
@@ -188,7 +188,68 @@ frontend::IsIdentifier(const char16_t* chars, size_t 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(ExclusiveContext* cx, uint32_t ln)
@@ -1169,38 +1230,6 @@ TokenStream::putIdentInTokenbuf(const char16_t* identStart)
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
@@ -1424,36 +1453,18 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
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)
+ if (!atom) {
goto error;
+ }
tp->type = TOK_NAME;
tp->setName(atom->asPropertyName());
goto out;
diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h
index e0119c83d..e92de4b03 100644
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -26,14 +26,13 @@
#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.
@@ -120,9 +119,6 @@ struct Token
// 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
@@ -164,10 +160,6 @@ struct Token
// 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;
@@ -224,11 +216,6 @@ struct Token
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 ||
@@ -254,10 +241,22 @@ struct Token
};
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.
@@ -344,25 +343,26 @@ class MOZ_STACK_CLASS TokenStream
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 {
@@ -498,9 +498,6 @@ class MOZ_STACK_CLASS TokenStream
{}
};
- bool awaitIsKeyword = false;
- friend class AutoAwaitIsKeyword;
-
uint32_t invalidTemplateEscapeOffset = 0;
InvalidEscapeType invalidTemplateEscapeType = InvalidEscapeType::None;
@@ -508,14 +505,12 @@ class MOZ_STACK_CLASS TokenStream
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
@@ -544,10 +539,6 @@ class MOZ_STACK_CLASS TokenStream
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");
}
@@ -574,12 +565,6 @@ class MOZ_STACK_CLASS TokenStream
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");
@@ -714,36 +699,6 @@ class MOZ_STACK_CLASS TokenStream
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))
@@ -809,19 +764,6 @@ class MOZ_STACK_CLASS TokenStream
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
@@ -1103,25 +1045,6 @@ class MOZ_STACK_CLASS TokenStream
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);