Title: [271509] trunk
Revision
271509
Author
[email protected]
Date
2021-01-14 21:55:48 -0800 (Thu, 14 Jan 2021)

Log Message

[JSC] Correctly handle escaped keyword identifiers
https://bugs.webkit.org/show_bug.cgi?id=220634

Reviewed by Yusuke Suzuki.

JSTests:

* stress/escaped-keyword-identifiers.js: Added.
* test262/expectations.yaml: Mark 16 test cases as passing.

Source/_javascript_Core:

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".

Modified Paths

Added Paths

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,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to