diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 4f9d0de2b6..114e68b1ef 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -830,6 +830,7 @@ NonnullRefPtr Parser::parse_object_expression() property_type = ObjectProperty::Type::KeyValue; RefPtr property_name; RefPtr property_value; + FunctionKind function_kind { FunctionKind::Regular }; if (match(TokenType::TripleDot)) { consume(); @@ -841,7 +842,12 @@ NonnullRefPtr Parser::parse_object_expression() continue; } - if (match(TokenType::Identifier)) { + if (match(TokenType::Asterisk)) { + consume(); + property_type = ObjectProperty::Type::KeyValue; + property_name = parse_property_key(); + function_kind = FunctionKind ::Generator; + } else if (match(TokenType::Identifier)) { auto identifier = consume().value(); if (identifier == "get" && match_property_key()) { property_type = ObjectProperty::Type::Getter; @@ -872,6 +878,8 @@ NonnullRefPtr Parser::parse_object_expression() parse_options |= FunctionNodeParseOptions::IsGetterFunction; if (property_type == ObjectProperty::Type::Setter) parse_options |= FunctionNodeParseOptions::IsSetterFunction; + if (function_kind == FunctionKind::Generator) + parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; auto function = parse_function_node(parse_options); properties.append(create_ast_node({ m_parser_state.m_current_token.filename(), rule_start.position(), position() }, *property_name, function, property_type, true)); } else if (match(TokenType::Colon)) { @@ -1380,13 +1388,15 @@ NonnullRefPtr Parser::parse_function_node(u8 parse_options) ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Function); - auto is_generator = false; + auto is_generator = (parse_options & FunctionNodeParseOptions::IsGeneratorFunction) != 0; String name; if (parse_options & FunctionNodeParseOptions::CheckForFunctionAndName) { consume(TokenType::Function); - is_generator = match(TokenType::Asterisk); - if (is_generator) - consume(TokenType::Asterisk); + if (!is_generator) { + is_generator = match(TokenType::Asterisk); + if (is_generator) + consume(TokenType::Asterisk); + } if (FunctionNodeType::must_have_name() || match(TokenType::Identifier)) name = consume(TokenType::Identifier).value(); diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 49e2d9904f..b1b364fcdc 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -29,6 +29,7 @@ struct FunctionNodeParseOptions { IsGetterFunction = 1 << 3, IsSetterFunction = 1 << 4, IsArrowFunction = 1 << 5, + IsGeneratorFunction = 1 << 6, }; }; diff --git a/Userland/Libraries/LibJS/Tests/syntax/generators.js b/Userland/Libraries/LibJS/Tests/syntax/generators.js index 70d45aa532..dbe0dcec4b 100644 --- a/Userland/Libraries/LibJS/Tests/syntax/generators.js +++ b/Userland/Libraries/LibJS/Tests/syntax/generators.js @@ -21,3 +21,21 @@ describe("parsing freestanding generators", () => { *bar; }`).toEval(); }); }); + +describe("parsing object literal generator functions", () => { + test("simple", () => { + expect(`x = { *foo() { } }`).toEval(); + expect(`x = { * foo() { } }`).toEval(); + expect(`x = { * + foo() { } }`).toEval(); + }); + test("yield", () => { + expect(`x = { foo() { yield; } }`).toEval(); + expect(`x = { *foo() { yield; } }`).toEval(); + expect(`x = { *foo() { yield 42; } }`).toEval(); + expect(`x = { foo() { yield 42; } }`).not.toEval(); + expect(`x = { *foo() { yield (yield); } }`).toEval(); + expect(`x = { * + foo() { yield (yield); } }`).toEval(); + }); +});