Forráskód Böngészése

LibJS: Allow escaped 'async' as identifier

Since 'async' is only special if it occurs before a function it can be
used as escaped identifier in all cases.
davidot 3 éve
szülő
commit
c57721cf83

+ 9 - 7
Userland/Libraries/LibJS/Parser.cpp

@@ -596,13 +596,15 @@ bool Parser::match_invalid_escaped_keyword() const
     if (m_state.current_token.type() != TokenType::EscapedKeyword)
         return false;
     auto token_value = m_state.current_token.value();
-    if (token_value == "await"sv) {
-        return m_program_type == Program::Type::Module;
-    }
-    if (m_state.strict_mode) {
+    if (token_value == "await"sv)
+        return m_program_type == Program::Type::Module || m_state.in_async_function_context;
+    if (token_value == "async"sv)
+        return false;
+    if (token_value == "yield"sv)
+        return m_state.in_generator_function_context;
+    if (m_state.strict_mode)
         return true;
-    }
-    return token_value != "yield"sv && token_value != "let"sv;
+    return token_value != "let"sv;
 }
 
 static constexpr AK::Array<StringView, 9> strict_reserved_words = { "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield" };
@@ -1230,7 +1232,7 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression()
             syntax_error("'super' keyword unexpected here");
         return { create_ast_node<SuperExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) };
     case TokenType::EscapedKeyword:
-        if (m_state.strict_mode || (m_state.current_token.value() != "let"sv && (m_state.in_generator_function_context || m_state.current_token.value() != "yield"sv) && (m_state.in_async_function_context || m_state.current_token.value() != "await"sv)))
+        if (match_invalid_escaped_keyword())
             syntax_error("Keyword must not contain escaped characters");
         [[fallthrough]];
     case TokenType::Identifier: {

+ 10 - 2
Userland/Libraries/LibJS/Tests/syntax/async-await.js

@@ -9,6 +9,10 @@ describe("parsing freestanding async functions", () => {
         expect(`async function foo() { await; }`).not.toEval();
         expect(`function foo() { await bar(); }`).not.toEval();
         expect(`function foo() { await; }`).toEval();
+
+        expect(`\\u0061sync function foo() { await bar(); }`).not.toEval();
+        expect(`\\u0061sync function foo() { \\u0061wait bar(); }`).not.toEval();
+        expect(`async function foo() { \\u0061wait bar(); }`).not.toEval();
     });
 });
 
@@ -82,11 +86,15 @@ describe("async arrow functions", () => {
         expect("async (b = await) => await b;").not.toEval();
         expect("async (b = await 3) => await b;").not.toEval();
 
-        // Cannot escape the async keyword.
+        // Cannot escape the async keyword and get an async arrow function.
         expect("\\u0061sync () => await 3").not.toEval();
 
-        expect("for (async of => {};;) {}").toEval();
+        expect("for (async of => {};false;) {}").toEval();
         expect("for (async of []) {}").not.toEval();
+
+        expect("for (\\u0061sync of []) {}").toEval();
+        expect("for (\\u0061sync of => {};false;) {}").not.toEval();
+        expect("for (\\u0061sync => {};false;) {}").toEval();
     });
 
     test("async within a for-loop", () => {