diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index a02e8d6b3a..53b4072591 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -627,11 +627,14 @@ NonnullRefPtr Parser::parse_primary_expression() switch (m_parser_state.m_current_token.type()) { case TokenType::ParenOpen: { + auto paren_position = position(); consume(TokenType::ParenOpen); - if (match(TokenType::ParenClose) || match(TokenType::Identifier) || match(TokenType::TripleDot)) { + if ((match(TokenType::ParenClose) || match(TokenType::Identifier) || match(TokenType::TripleDot)) && !try_parse_arrow_function_expression_failed_at_position(paren_position)) { auto arrow_function_result = try_parse_arrow_function_expression(true); if (!arrow_function_result.is_null()) return arrow_function_result.release_nonnull(); + + set_try_parse_arrow_function_expression_failed_at_position(paren_position, true); } auto expression = parse_expression(0); consume(TokenType::ParenClose); @@ -651,9 +654,13 @@ NonnullRefPtr Parser::parse_primary_expression() syntax_error("'super' keyword unexpected here"); return create_ast_node({ m_parser_state.m_current_token.filename(), rule_start.position(), position() }); case TokenType::Identifier: { - auto arrow_function_result = try_parse_arrow_function_expression(false); - if (!arrow_function_result.is_null()) - return arrow_function_result.release_nonnull(); + if (!try_parse_arrow_function_expression_failed_at_position(position())) { + auto arrow_function_result = try_parse_arrow_function_expression(false); + if (!arrow_function_result.is_null()) + return arrow_function_result.release_nonnull(); + + set_try_parse_arrow_function_expression_failed_at_position(position(), true); + } return create_ast_node({ m_parser_state.m_current_token.filename(), rule_start.position(), position() }, consume().value()); } case TokenType::NumericLiteral: @@ -2039,6 +2046,20 @@ Position Parser::position() const }; } +bool Parser::try_parse_arrow_function_expression_failed_at_position(const Position& position) const +{ + auto it = m_token_memoizations.find(position); + if (it == m_token_memoizations.end()) + return false; + + return (*it).value.try_parse_arrow_function_expression_failed; +} + +void Parser::set_try_parse_arrow_function_expression_failed_at_position(const Position& position, bool failed) +{ + m_token_memoizations.set(position, { failed }); +} + void Parser::syntax_error(const String& message, Optional position) { if (!position.has_value()) diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index cf066f65ee..e64e41dce0 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -147,6 +147,10 @@ public: } } + struct TokenMemoization { + bool try_parse_arrow_function_expression_failed; + }; + private: friend class ScopePusher; @@ -172,6 +176,9 @@ private: void discard_saved_state(); Position position() const; + bool try_parse_arrow_function_expression_failed_at_position(const Position&) const; + void set_try_parse_arrow_function_expression_failed_at_position(const Position&, bool); + struct RulePosition { AK_MAKE_NONCOPYABLE(RulePosition); AK_MAKE_NONMOVABLE(RulePosition); @@ -220,9 +227,23 @@ private: explicit ParserState(Lexer); }; + class PositionKeyTraits { + public: + static int hash(const Position& position) + { + return int_hash(position.line) ^ int_hash(position.column); + } + + static bool equals(const Position& a, const Position& b) + { + return a.column == b.column && a.line == b.line; + } + }; + Vector m_rule_starts; ParserState m_parser_state; FlyString m_filename; Vector m_saved_state; + HashMap m_token_memoizations; }; }