Diff
Modified: trunk/JSTests/ChangeLog (271508 => 271509)
--- trunk/JSTests/ChangeLog 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/JSTests/ChangeLog 2021-01-15 05:55:48 UTC (rev 271509)
@@ -1,3 +1,13 @@
+2021-01-14 Ross Kirsling <[email protected]>
+
+ [JSC] Correctly handle escaped keyword identifiers
+ https://bugs.webkit.org/show_bug.cgi?id=220634
+
+ Reviewed by Yusuke Suzuki.
+
+ * stress/escaped-keyword-identifiers.js: Added.
+ * test262/expectations.yaml: Mark 16 test cases as passing.
+
2021-01-12 Ross Kirsling <[email protected]>
[JSC] Class name 'await' is valid in sync context
Added: trunk/JSTests/stress/escaped-keyword-identifiers.js (0 => 271509)
--- trunk/JSTests/stress/escaped-keyword-identifiers.js (rev 0)
+++ trunk/JSTests/stress/escaped-keyword-identifiers.js 2021-01-15 05:55:48 UTC (rev 271509)
@@ -0,0 +1,80 @@
+function shouldNotThrow(func) {
+ func();
+}
+
+function shouldThrowSyntaxError(script) {
+ let error;
+ try {
+ eval(script);
+ } catch (e) {
+ error = e;
+ }
+
+ if (!(error instanceof SyntaxError))
+ throw new Error('Expected SyntaxError!');
+}
+
+shouldNotThrow(() => { l\u0065t: 3; });
+shouldNotThrow(() => { aw\u0061it: 3; });
+shouldNotThrow(() => { yi\u0065ld: 3; });
+shouldNotThrow(() => { st\u0061tic: 3; });
+shouldThrowSyntaxError('nu\\u006cl: 3;');
+shouldThrowSyntaxError('async function f() { aw\\u0061it: 3; }');
+shouldThrowSyntaxError('function* g() { yi\\u0065ld: 3; }');
+
+shouldNotThrow(() => { var l\u0065t = 3; });
+shouldNotThrow(() => { var aw\u0061it = 3; });
+shouldNotThrow(() => { var yi\u0065ld = 3; });
+shouldNotThrow(() => { var st\u0061tic = 3; });
+shouldThrowSyntaxError('var nu\\u006cl = 3;');
+shouldThrowSyntaxError('async function f() { var aw\\u0061it = 3; }');
+shouldThrowSyntaxError('function* g() { var yi\\u0065ld = 3; }');
+
+shouldNotThrow(() => { let aw\u0061it = 3; });
+shouldNotThrow(() => { let yi\u0065ld = 3; });
+shouldNotThrow(() => { let st\u0061tic = 3; });
+shouldThrowSyntaxError('let l\\u0065t = 3;');
+shouldThrowSyntaxError('let nu\\u006cl = 3;');
+shouldThrowSyntaxError('async function f() { let aw\\u0061it = 3; }');
+shouldThrowSyntaxError('function* g() { let yi\\u0065ld = 3; }');
+
+shouldNotThrow(() => { const aw\u0061it = 3; });
+shouldNotThrow(() => { const yi\u0065ld = 3; });
+shouldNotThrow(() => { const st\u0061tic = 3; });
+shouldThrowSyntaxError('const l\\u0065t = 3;');
+shouldThrowSyntaxError('const nu\\u006cl = 3;');
+shouldThrowSyntaxError('async function f() { const aw\\u0061it = 3; }');
+shouldThrowSyntaxError('function* g() { const yi\\u0065ld = 3; }');
+
+shouldNotThrow(() => { class aw\u0061it {} });
+shouldThrowSyntaxError('class l\\u0065t {}');
+shouldThrowSyntaxError('class yi\\u0065ld {}');
+shouldThrowSyntaxError('class st\\u0061tic {}');
+shouldThrowSyntaxError('class nu\\u006cl {}');
+shouldThrowSyntaxError('async function f() { class aw\\u0061it {} }');
+shouldThrowSyntaxError('function* g() { class yi\\u0065ld {} }');
+
+shouldNotThrow(() => { async function aw\u0061it() {} });
+shouldNotThrow(() => { function* yi\u0065ld() {} });
+shouldThrowSyntaxError('async function f() { function aw\\u0061it() {} }');
+shouldThrowSyntaxError('function* g() { function yi\\u0065ld() {} }');
+
+shouldNotThrow(() => { function f(aw\u0061it) {} });
+shouldNotThrow(() => { function g(yi\u0065ld) {} });
+shouldThrowSyntaxError('async function f(aw\\u0061it) {}');
+shouldThrowSyntaxError('function* g(yi\\u0065ld) {}');
+
+shouldNotThrow(() => { function l\u0065t() {} });
+shouldNotThrow(() => { function st\u0061tic() {} });
+shouldNotThrow(() => { function f(l\u0065t) {} });
+shouldNotThrow(() => { function f(st\u0061tic) {} });
+shouldThrowSyntaxError('function f() { function nu\\u006cl() {} }');
+shouldThrowSyntaxError('function f(nu\\u006cl) {}');
+
+shouldNotThrow(() => { l\u0065t => 3; });
+shouldNotThrow(() => { aw\u0061it => 3; });
+shouldNotThrow(() => { yi\u0065ld => 3; });
+shouldNotThrow(() => { st\u0061tic => 3; });
+shouldThrowSyntaxError('nu\\u006cl => 3;');
+shouldThrowSyntaxError('async function f() { aw\\u0061it => 3; }');
+shouldThrowSyntaxError('function* g() { yi\\u0065ld => 3; }');
Modified: trunk/JSTests/test262/expectations.yaml (271508 => 271509)
--- trunk/JSTests/test262/expectations.yaml 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/JSTests/test262/expectations.yaml 2021-01-15 05:55:48 UTC (rev 271509)
@@ -1478,9 +1478,6 @@
default: 'RangeError: Maximum call stack size exceeded.'
test/language/expressions/call/tco-non-eval-with.js:
default: 'RangeError: Maximum call stack size exceeded.'
-test/language/expressions/class/class-name-ident-await-escaped.js:
- default: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
- strict mode: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
test/language/expressions/class/elements/arrow-body-direct-eval-err-contains-arguments.js:
default: 'Test262Error: Expected a SyntaxError but got a ReferenceError'
strict mode: 'Test262Error: Expected a SyntaxError but got a ReferenceError'
@@ -1703,18 +1700,6 @@
strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
test/language/identifier-resolution/assign-to-global-undefined.js:
strict mode: Expected uncaught exception with name 'ReferenceError' but none was thrown
-test/language/identifiers/part-unicode-13.0.0-escaped.js:
- default: "SyntaxError: Invalid unicode escape in identifier: '_\\u0B55'"
- strict mode: "SyntaxError: Invalid unicode escape in identifier: '_\\u0B55'"
-test/language/identifiers/part-unicode-13.0.0.js:
- default: "SyntaxError: Invalid character '\\u0b55'"
- strict mode: "SyntaxError: Invalid character '\\u0b55'"
-test/language/identifiers/start-unicode-13.0.0-escaped.js:
- default: "SyntaxError: Invalid unicode escape in identifier: '\\u08BE'"
- strict mode: "SyntaxError: Invalid unicode escape in identifier: '\\u08BE'"
-test/language/identifiers/start-unicode-13.0.0.js:
- default: "SyntaxError: Invalid character '\\u08be'"
- strict mode: "SyntaxError: Invalid character '\\u08be'"
test/language/literals/regexp/u-astral-char-class-invert.js:
default: 'Test262Error: Expected SameValue(«�», «null») to be true'
strict mode: 'Test262Error: Expected SameValue(«�», «null») to be true'
@@ -1730,9 +1715,6 @@
test/language/statements/async-generator/return-undefined-implicit-and-explicit.js:
default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [tick 1, g1 ret, tick 2, g2 ret, g3 ret, g4 ret] and [tick 1, g1 ret, g2 ret, tick 2, g3 ret, g4 ret] to have the same contents. Ticks for implicit and explicit return undefined'
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: Expected [tick 1, g1 ret, tick 2, g2 ret, g3 ret, g4 ret] and [tick 1, g1 ret, g2 ret, tick 2, g3 ret, g4 ret] to have the same contents. Ticks for implicit and explicit return undefined'
-test/language/statements/class/class-name-ident-await-escaped.js:
- default: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
- strict mode: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
test/language/statements/class/elements/arrow-body-direct-eval-err-contains-arguments.js:
default: 'Test262Error: Expected a SyntaxError but got a ReferenceError'
strict mode: 'Test262Error: Expected a SyntaxError but got a ReferenceError'
@@ -1888,11 +1870,6 @@
strict mode: 'Test262: This statement should not be evaluated.'
test/language/statements/labeled/let-array-with-newline.js:
default: 'Test262: This statement should not be evaluated.'
-test/language/statements/labeled/value-await-non-module-escaped.js:
- default: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
- strict mode: "SyntaxError: Unexpected escaped characters in keyword token: 'aw\\u0061it'"
-test/language/statements/labeled/value-yield-non-strict-escaped.js:
- default: "SyntaxError: Unexpected escaped characters in keyword token: 'yi\\u0065ld'"
test/language/statements/let/block-local-closure-set-before-initialization.js:
default: 'Test262Error: Expected a ReferenceError to be thrown but no exception was thrown at all'
test/language/statements/let/dstr/ary-init-iter-get-err-array-prototype.js:
@@ -1901,8 +1878,6 @@
test/language/statements/let/dstr/ary-ptrn-elem-id-iter-val-array-prototype.js:
default: 'Test262Error: Expected SameValue(«3», «42») to be true'
strict mode: 'Test262Error: Expected SameValue(«3», «42») to be true'
-test/language/statements/let/syntax/escaped-let.js:
- default: "SyntaxError: Unexpected escaped characters in keyword token: 'l\\u0065t'"
test/language/statements/switch/scope-lex-async-function.js:
default: Expected uncaught exception with name 'ReferenceError' but none was thrown
test/language/statements/switch/scope-lex-async-generator.js:
Modified: trunk/Source/_javascript_Core/ChangeLog (271508 => 271509)
--- trunk/Source/_javascript_Core/ChangeLog 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/Source/_javascript_Core/ChangeLog 2021-01-15 05:55:48 UTC (rev 271509)
@@ -1,3 +1,47 @@
+2021-01-14 Ross Kirsling <[email protected]>
+
+ [JSC] Correctly handle escaped keyword identifiers
+ https://bugs.webkit.org/show_bug.cgi?id=220634
+
+ Reviewed by Yusuke Suzuki.
+
+ When `let`, `await`, and `yield` are accepted as identifiers, they should be accepted even in escaped form.
+ This patch ensures this behavior for variable, parameter, and label names.
+
+ * parser/Parser.cpp:
+ (JSC::Parser<LexerType>::isArrowFunctionParameters):
+ (JSC::Parser<LexerType>::parseStatementListItem):
+ (JSC::Parser<LexerType>::parseVariableDeclarationList):
+ (JSC::Parser<LexerType>::parseFormalParameters):
+ (JSC::Parser<LexerType>::parseFunctionInfo):
+ (JSC::Parser<LexerType>::parseClass):
+ (JSC::Parser<LexerType>::parseAssignmentExpression):
+ (JSC::Parser<LexerType>::parsePrimaryExpression):
+ Make use of new parser functions.
+
+ * parser/Parser.h:
+ (JSC::isContextualKeyword): Renamed from isAnyContextualKeyword.
+ (JSC::Parser::matchSpecIdentifier): Allow escaped contextual keywords.
+ (JSC::Parser::matchIdentifierOrPossiblyEscapedContextualKeyword): Added.
+ (JSC::Parser::isAllowedIdentifierLet): Renamed from isLETMaskedAsIDENT.
+ (JSC::Parser::isPossiblyEscapedLet): Added.
+ (JSC::Parser::isDisallowedIdentifierAwait): Added.
+ (JSC::Parser::isAllowedIdentifierAwait): Added.
+ (JSC::Parser::isPossiblyEscapedAwait): Added.
+ (JSC::Parser::canUseIdentifierAwait): Added.
+ (JSC::Parser::isDisallowedIdentifierYield): Added.
+ (JSC::Parser::isAllowedIdentifierYield): Renamed from isYIELDMaskedAsIDENT.
+ (JSC::Parser::isPossiblyEscapedYield): Added.
+ (JSC::Parser::canUseIdentifierYield): Added.
+ (JSC::Parser::matchAllowedEscapedContextualKeyword): Added.
+ (JSC::Parser::disallowedIdentifierAwaitReason): Fix mistake (left over from previous patch).
+ (JSC::isIdentifierOrAnyContextualKeyword): Deleted.
+ (JSC::isSafeContextualKeyword): Deleted.
+ (JSC::Parser::isDisallowedIdentifierLet): Deleted.
+
+ * parser/ParserTokens.h:
+ Remove obsolete notion of "safe contextual keyword".
+
2021-01-14 Xan Lopez <[email protected]>
[JSC] Implement a B3::ValueRep replacement for wasm-llint
Modified: trunk/Source/_javascript_Core/parser/Parser.cpp (271508 => 271509)
--- trunk/Source/_javascript_Core/parser/Parser.cpp 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/Source/_javascript_Core/parser/Parser.cpp 2021-01-15 05:55:48 UTC (rev 271509)
@@ -74,13 +74,13 @@
if (token.m_type == RESERVED || token.m_type == RESERVED_IF_STRICT) \
semanticFail("Cannot use the reserved word '", getToken(token), "' as a ", __VA_ARGS__); \
if (token.m_type & KeywordTokenFlag) { \
- if (!isAnyContextualKeyword(token)) \
+ if (!isContextualKeyword(token)) \
semanticFail("Cannot use the keyword '", getToken(token), "' as a ", __VA_ARGS__); \
- if (isDisallowedIdentifierLet(token)) \
+ if (token.m_type == LET && strictMode())\
semanticFail("Cannot use 'let' as a ", __VA_ARGS__, " ", disallowedIdentifierLetReason()); \
- if (isDisallowedIdentifierAwait(token)) \
+ if (token.m_type == AWAIT && !canUseIdentifierAwait()) \
semanticFail("Cannot use 'await' as a ", __VA_ARGS__, " ", disallowedIdentifierAwaitReason()); \
- if (isDisallowedIdentifierYield(token)) \
+ if (token.m_type == YIELD && !canUseIdentifierYield()) \
semanticFail("Cannot use 'yield' as a ", __VA_ARGS__, " ", disallowedIdentifierYieldReason()); \
} \
} while (0)
@@ -337,7 +337,7 @@
}
if (matchSpecIdentifier()) {
- semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Cannot use 'await' as a parameter name in an async function");
+ semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a parameter name in an async function");
SavePoint saveArrowFunctionPoint = createSavePoint(context);
next();
bool isArrowFunction = match(ARROWFUNCTION);
@@ -673,12 +673,12 @@
if (!strictMode()) {
SavePoint savePoint = createSavePoint(context);
next();
- // Intentionally use `isIdentifierOrAnyContextualKeyword(m_token)` and don't use `matchSpecIdentifier()`.
- // We would like to fall into parseVariableDeclaration path even if "yield" is not treated as an Identifier.
+ // Intentionally use `matchIdentifierOrPossiblyEscapedContextualKeyword()` and not `matchSpecIdentifier()`.
+ // We would like contextual keywords to fall under parseVariableDeclaration even when not used as identifiers.
// For example, under a generator context, matchSpecIdentifier() for "yield" returns `false`.
// But we would like to enter parseVariableDeclaration and raise an error under the context of parseVariableDeclaration
// to raise consistent errors between "var", "const" and "let".
- if (!isIdentifierOrAnyContextualKeyword(m_token) && !match(OPENBRACE) && !match(OPENBRACKET))
+ if (!matchIdentifierOrPossiblyEscapedContextualKeyword() && !match(OPENBRACE) && !match(OPENBRACKET))
shouldParseVariableDeclaration = false;
restoreSavePoint(context, savePoint);
}
@@ -697,6 +697,12 @@
case FUNCTION:
result = parseFunctionDeclaration(context);
break;
+ case ESCAPED_KEYWORD:
+ if (!matchAllowedEscapedContextualKeyword()) {
+ failDueToUnexpectedToken();
+ break;
+ }
+ FALLTHROUGH;
case IDENT:
if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
// Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
@@ -831,7 +837,7 @@
failIfTrue(match(PRIVATENAME), "Cannot use a private name to declare a variable");
if (matchSpecIdentifier()) {
- failIfTrue(match(LET) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration),
+ failIfTrue(isPossiblyEscapedLet(m_token) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration),
"Cannot use 'let' as an identifier name for a LexicalDeclaration");
semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a ", declarationTypeToVariableKind(declarationType), " ", disallowedIdentifierAwaitReason());
JSTextPosition varStart = tokenStartPosition();
@@ -2093,7 +2099,7 @@
if (match(DOTDOTDOT)) {
next();
- semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Cannot use 'await' as a parameter name in an async function");
+ semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a parameter name in an async function");
TreeDestructuringPattern destructuringPattern = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern);
propagateError();
parameter = context.createRestParameter(destructuringPattern, restParameterStart);
@@ -2339,8 +2345,8 @@
ScopeRef parentScope = currentScope();
- bool isDisallowedAwaitFunctionName = isDisallowedIdentifierAwait(m_token);
- const char* isDisallowedAwaitFunctionNameReason = isDisallowedAwaitFunctionName ? disallowedIdentifierAwaitReason() : nullptr;
+ bool functionNameIsAwait = isPossiblyEscapedAwait(m_token);
+ const char* isDisallowedAwaitFunctionNameReason = functionNameIsAwait && !canUseIdentifierAwait() ? disallowedIdentifierAwaitReason() : nullptr;
AutoPopScopeRef functionScope(this, pushScope());
functionScope->setSourceParseMode(mode);
@@ -2482,16 +2488,16 @@
// function * BindingIdentifier[Yield]opt ( FormalParameters[Yield] ) { GeneratorBody }
//
// The name of FunctionExpression and AsyncFunctionExpression can accept "yield" even in the context of generator.
- bool upperScopeIsGenerator = false;
+ bool canUseYield = !strictMode();
if (!(functionDefinitionType == FunctionDefinitionType::_expression_ && SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::AsyncFunctionMode).contains(mode)))
- upperScopeIsGenerator = upperScope(1)->isGenerator();
+ canUseYield &= !parentScope->isGenerator();
if (requirements != FunctionNameRequirements::Unnamed) {
ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !functionInfo.name), "When specifying FunctionNameRequirements::None, we need to initialize functionInfo.name with the default value in the caller side.");
- if (matchSpecIdentifier(upperScopeIsGenerator)) {
+ if (matchSpecIdentifier(canUseYield, functionNameIsAwait)) {
functionInfo.name = m_token.m_data.ident;
m_parserState.lastFunctionName = functionInfo.name;
- if (UNLIKELY(isDisallowedAwaitFunctionName))
+ if (UNLIKELY(isDisallowedAwaitFunctionNameReason))
semanticFailIfTrue(functionDefinitionType == FunctionDefinitionType::Declaration || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode), "Cannot declare function named 'await' ", isDisallowedAwaitFunctionNameReason);
else if (isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode) && match(AWAIT) && functionDefinitionType == FunctionDefinitionType::_expression_)
semanticFail("Cannot declare ", stringForFunctionMode(mode), " named 'await'");
@@ -2860,7 +2866,7 @@
ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax.");
ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side.");
- if (match(IDENT) || (match(AWAIT) && !isDisallowedIdentifierAwait(m_token))) {
+ if (match(IDENT) || isAllowedIdentifierAwait(m_token)) {
info.className = m_token.m_data.ident;
next();
failIfTrue(classScope->declareLexicalVariable(info.className, true) & DeclarationResult::InvalidStrictMode, "'", info.className->impl(), "' is not a valid class name");
@@ -3898,7 +3904,7 @@
failIfStackOverflow();
- if (match(YIELD) && !isYIELDMaskedAsIDENT(currentScope()->isGenerator()))
+ if (match(YIELD) && !canUseIdentifierYield())
return parseYieldExpression(context);
JSTextPosition start = tokenStartPosition();
@@ -3909,7 +3915,7 @@
bool wasOpenParen = match(OPENPAREN);
// Do not use matchSpecIdentifier() here since it is slower than isIdentifierOrKeyword.
// Whether spec identifier is will be validated by isArrowFunctionParameters().
- bool wasIdentifierOrKeyword = isIdentifierOrKeyword(m_token);
+ bool wasIdentifierOrKeyword = matchIdentifierOrKeyword() || (m_token.m_type == ESCAPED_KEYWORD);
bool maybeValidArrowFunctionStart = wasOpenParen || wasIdentifierOrKeyword;
SavePoint savePoint = createSavePoint(context);
size_t usedVariablesSize = 0;
@@ -3970,7 +3976,7 @@
return lhs;
}
-
+
int assignmentStack = 0;
Operator op;
bool hadAssignment = false;
@@ -4813,12 +4819,16 @@
case BACKQUOTE:
return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings);
case YIELD:
- if (!strictMode() && !currentScope()->isGenerator())
+ if (canUseIdentifierYield())
goto identifierExpression;
failDueToUnexpectedToken();
case LET:
if (!strictMode())
goto identifierExpression;
+ failDueToUnexpectedToken();
+ case ESCAPED_KEYWORD:
+ if (matchAllowedEscapedContextualKeyword())
+ goto identifierExpression;
FALLTHROUGH;
default:
failDueToUnexpectedToken();
Modified: trunk/Source/_javascript_Core/parser/Parser.h (271508 => 271509)
--- trunk/Source/_javascript_Core/parser/Parser.h 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/Source/_javascript_Core/parser/Parser.h 2021-01-15 05:55:48 UTC (rev 271509)
@@ -133,22 +133,11 @@
{
return token.m_type == IDENT || token.m_type & KeywordTokenFlag;
}
-// _Any_ContextualKeyword includes keywords such as "let" or "yield", which have a specific meaning depending on the current parse mode
-// or strict mode. These helpers allow to treat all contextual keywords as identifiers as required.
-ALWAYS_INLINE static bool isAnyContextualKeyword(const JSToken& token)
+// "let", "yield", and "await" may be keywords or identifiers depending on context.
+ALWAYS_INLINE static bool isContextualKeyword(const JSToken& token)
{
return token.m_type >= FirstContextualKeywordToken && token.m_type <= LastContextualKeywordToken;
}
-ALWAYS_INLINE static bool isIdentifierOrAnyContextualKeyword(const JSToken& token)
-{
- return token.m_type == IDENT || isAnyContextualKeyword(token);
-}
-// _Safe_ContextualKeyword includes only contextual keywords which can be treated as identifiers independently from parse mode. The exeption
-// to this rule is `await`, but matchSpecIdentifier() always treats it as an identifier regardless.
-ALWAYS_INLINE static bool isSafeContextualKeyword(const JSToken& token)
-{
- return token.m_type >= FirstSafeContextualKeywordToken && token.m_type <= LastSafeContextualKeywordToken;
-}
JS_EXPORT_PRIVATE extern std::atomic<unsigned> globalParseCount;
@@ -1684,29 +1673,22 @@
return result;
}
- // http://ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors
- ALWAYS_INLINE bool isLETMaskedAsIDENT()
+ ALWAYS_INLINE bool matchSpecIdentifier()
{
- return match(LET) && !strictMode();
+ return match(IDENT) || isAllowedIdentifierLet(m_token) || isAllowedIdentifierYield(m_token) || isPossiblyEscapedAwait(m_token);
}
- // http://ecma-international.org/ecma-262/6.0/#sec-identifiers-static-semantics-early-errors
- ALWAYS_INLINE bool isYIELDMaskedAsIDENT(bool inGenerator)
+ // Special case where some information is already known.
+ ALWAYS_INLINE bool matchSpecIdentifier(bool canUseYield, bool isAwait)
{
- return match(YIELD) && !strictMode() && !inGenerator;
+ return isAwait || match(IDENT) || isAllowedIdentifierLet(m_token) || (canUseYield && isPossiblyEscapedYield(m_token));
}
- // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
- ALWAYS_INLINE bool matchSpecIdentifier(bool inGenerator)
+ ALWAYS_INLINE bool matchIdentifierOrPossiblyEscapedContextualKeyword()
{
- return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(inGenerator) || isSafeContextualKeyword(m_token);
+ return match(IDENT) || isPossiblyEscapedLet(m_token) || isPossiblyEscapedYield(m_token) || isPossiblyEscapedAwait(m_token);
}
- ALWAYS_INLINE bool matchSpecIdentifier()
- {
- return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(currentScope()->isGenerator()) || isSafeContextualKeyword(m_token);
- }
-
template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode);
template <class TreeBuilder> TreeSourceElements parseGeneratorFunctionSourceElements(TreeBuilder&, const Identifier& name, SourceElementsMode);
template <class TreeBuilder> TreeSourceElements parseAsyncFunctionSourceElements(TreeBuilder&, SourceParseMode, bool isArrowFunctionBodyExpression, SourceElementsMode);
@@ -1835,20 +1817,63 @@
return !m_errorMessage.isNull();
}
- bool isDisallowedIdentifierLet(const JSToken& token)
+ bool isAllowedIdentifierLet(const JSToken& token)
{
- return token.m_type == LET && strictMode();
+ return isPossiblyEscapedLet(token) && !strictMode();
}
+ ALWAYS_INLINE bool isPossiblyEscapedLet(const JSToken& token)
+ {
+ return token.m_type == LET || UNLIKELY(token.m_type == ESCAPED_KEYWORD && *token.m_data.ident == m_vm.propertyNames->letKeyword);
+ }
+
bool isDisallowedIdentifierAwait(const JSToken& token)
{
- return token.m_type == AWAIT && (!m_parserState.allowAwait || currentScope()->isAsyncFunction() || m_scriptMode == JSParserScriptMode::Module);
+ return isPossiblyEscapedAwait(token) && !canUseIdentifierAwait();
}
+ bool isAllowedIdentifierAwait(const JSToken& token)
+ {
+ return isPossiblyEscapedAwait(token) && canUseIdentifierAwait();
+ }
+
+ ALWAYS_INLINE bool isPossiblyEscapedAwait(const JSToken& token)
+ {
+ return token.m_type == AWAIT || UNLIKELY(token.m_type == ESCAPED_KEYWORD && *token.m_data.ident == m_vm.propertyNames->awaitKeyword);
+ }
+
+ ALWAYS_INLINE bool canUseIdentifierAwait()
+ {
+ return m_parserState.allowAwait && !currentScope()->isAsyncFunction() && m_scriptMode != JSParserScriptMode::Module;
+ }
+
bool isDisallowedIdentifierYield(const JSToken& token)
{
- return token.m_type == YIELD && (strictMode() || currentScope()->isGenerator());
+ return isPossiblyEscapedYield(token) && !canUseIdentifierYield();
}
+
+ bool isAllowedIdentifierYield(const JSToken& token)
+ {
+ return isPossiblyEscapedYield(token) && canUseIdentifierYield();
+ }
+
+ ALWAYS_INLINE bool isPossiblyEscapedYield(const JSToken& token)
+ {
+ return token.m_type == YIELD || UNLIKELY(token.m_type == ESCAPED_KEYWORD && *token.m_data.ident == m_vm.propertyNames->yieldKeyword);
+ }
+
+ ALWAYS_INLINE bool canUseIdentifierYield()
+ {
+ return !strictMode() && !currentScope()->isGenerator();
+ }
+
+ bool matchAllowedEscapedContextualKeyword()
+ {
+ ASSERT(m_token.m_type == ESCAPED_KEYWORD);
+ return (*m_token.m_data.ident == m_vm.propertyNames->letKeyword && !strictMode())
+ || (*m_token.m_data.ident == m_vm.propertyNames->awaitKeyword && canUseIdentifierAwait())
+ || (*m_token.m_data.ident == m_vm.propertyNames->yieldKeyword && canUseIdentifierYield());
+ }
ALWAYS_INLINE SuperBinding adjustSuperBindingForBaseConstructor(ConstructorKind constructorKind, SuperBinding superBinding, ScopeRef functionScope)
{
@@ -1875,7 +1900,7 @@
const char* disallowedIdentifierAwaitReason()
{
- if (!m_parserState.allowAwait || currentScope()->isAsyncFunctionBoundary())
+ if (!m_parserState.allowAwait || currentScope()->isAsyncFunction())
return "in an async function";
if (m_scriptMode == JSParserScriptMode::Module)
return "in a module";
Modified: trunk/Source/_javascript_Core/parser/ParserTokens.h (271508 => 271509)
--- trunk/Source/_javascript_Core/parser/ParserTokens.h 2021-01-15 05:12:47 UTC (rev 271508)
+++ trunk/Source/_javascript_Core/parser/ParserTokens.h 2021-01-15 05:55:48 UTC (rev 271509)
@@ -98,8 +98,6 @@
FirstContextualKeywordToken = LET,
LastContextualKeywordToken = AWAIT,
- FirstSafeContextualKeywordToken = AWAIT,
- LastSafeContextualKeywordToken = LastContextualKeywordToken,
OPENBRACE = 0,
CLOSEBRACE,