diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 2e5faf424f..27e3a6ff19 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1134,9 +1134,10 @@ private: class ObjectExpression final : public Expression { public: - explicit ObjectExpression(SourceRange source_range, NonnullRefPtrVector properties = {}) + explicit ObjectExpression(SourceRange source_range, NonnullRefPtrVector properties = {}, Optional first_invalid_property_range = {}) : Expression(source_range) , m_properties(move(properties)) + , m_first_invalid_property_range(move(first_invalid_property_range)) { } @@ -1144,8 +1145,11 @@ public: virtual void dump(int indent) const override; virtual void generate_bytecode(Bytecode::Generator&) const override; + Optional const& invalid_property_range() const { return m_first_invalid_property_range; } + private: NonnullRefPtrVector m_properties; + Optional m_first_invalid_property_range; }; class ArrayExpression final : public Expression { diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 45e6ead374..feaf0ec9bc 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -881,6 +881,7 @@ NonnullRefPtr Parser::parse_object_expression() NonnullRefPtrVector properties; ObjectProperty::Type property_type; + Optional invalid_object_literal_property_range; auto skip_to_next_property = [&] { while (!done() && !match(TokenType::Comma) && !match(TokenType::CurlyOpen)) @@ -931,8 +932,14 @@ NonnullRefPtr Parser::parse_object_expression() continue; } } - - if (match(TokenType::ParenOpen)) { + if (match(TokenType::Equals)) { + // Not a valid object literal, but a valid assignment target + consume(); + // Parse the expression and throw it away + auto expression = parse_expression(2); + if (!invalid_object_literal_property_range.has_value()) + invalid_object_literal_property_range = expression->source_range(); + } else if (match(TokenType::ParenOpen)) { VERIFY(property_name); u8 parse_options = FunctionNodeParseOptions::AllowSuperPropertyLookup; if (property_type == ObjectProperty::Type::Getter) @@ -965,7 +972,10 @@ NonnullRefPtr Parser::parse_object_expression() } consume(TokenType::CurlyClose); - return create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, properties); + return create_ast_node( + { m_state.current_token.filename(), rule_start.position(), position() }, + move(properties), + move(invalid_object_literal_property_range)); } NonnullRefPtr Parser::parse_array_expression() @@ -1086,6 +1096,12 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati { auto rule_start = push_start(); auto [expression, should_continue_parsing] = parse_primary_expression(); + auto check_for_invalid_object_property = [&](auto& expression) { + if (is(*expression)) { + if (auto range = static_cast(*expression).invalid_property_range(); range.has_value()) + syntax_error("Invalid property in object literal", range->start); + } + }; while (match(TokenType::TemplateLiteralStart)) { auto template_literal = parse_template_literal(true); expression = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }, move(expression), move(template_literal)); @@ -1097,6 +1113,7 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati break; if (new_precedence == min_precedence && associativity == Associativity::Left) break; + check_for_invalid_object_property(expression); Associativity new_associativity = operator_associativity(m_state.current_token.type()); expression = parse_secondary_expression(move(expression), new_precedence, new_associativity); @@ -1106,6 +1123,9 @@ NonnullRefPtr Parser::parse_expression(int min_precedence, Associati } } } + + check_for_invalid_object_property(expression); + if (match(TokenType::Comma) && min_precedence <= 1) { NonnullRefPtrVector expressions; expressions.append(expression);