diff options
author | davidot <david.tuin@gmail.com> | 2021-07-25 01:01:22 +0200 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-08-16 23:20:04 +0100 |
commit | b16c02d6b4ce1d5cd226314ea268592c56270aaf (patch) | |
tree | aa5fb19b478a6b8af5f88e68679c0d4a1357a942 | |
parent | 4cc95ae39db7a63846d75ac5f161c183220ff129 (diff) | |
download | serenity-b16c02d6b4ce1d5cd226314ea268592c56270aaf.zip |
LibJS: Allow labelled functions in certain contexts
Also disallow duplicated labels.
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 46 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.h | 10 |
2 files changed, 45 insertions, 11 deletions
diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 2de0fde491..5c259e7332 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -298,7 +298,7 @@ NonnullRefPtr<Program> Parser::parse_program(bool starts_in_strict_mode) program->append(parse_declaration()); parsing_directives = false; } else if (match_statement()) { - auto statement = parse_statement(); + auto statement = parse_statement(AllowLabelledFunction::Yes); program->append(statement); if (statement_is_use_strict_directive(statement)) { if (parsing_directives) { @@ -363,7 +363,7 @@ NonnullRefPtr<Declaration> Parser::parse_declaration() } } -NonnullRefPtr<Statement> Parser::parse_statement() +NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_labelled_function) { auto rule_start = push_start(); switch (m_state.current_token.type()) { @@ -401,8 +401,8 @@ NonnullRefPtr<Statement> Parser::parse_statement() consume(); return create_ast_node<EmptyStatement>({ m_state.current_token.filename(), rule_start.position(), position() }); default: - if (match(TokenType::Identifier)) { - auto result = try_parse_labelled_statement(); + if (match_identifier_name()) { + auto result = try_parse_labelled_statement(allow_labelled_function); if (!result.is_null()) return result.release_nonnull(); } @@ -534,7 +534,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe move(parameters), function_length, FunctionKind::Regular, is_strict, true); } -RefPtr<Statement> Parser::try_parse_labelled_statement() +RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) { save_state(); auto rule_start = push_start(); @@ -542,6 +542,11 @@ RefPtr<Statement> Parser::try_parse_labelled_statement() load_state(); }; + if (match(TokenType::Yield) && (m_state.strict_mode || m_state.in_generator_function_context)) { + syntax_error("'yield' label not allowed in this context"); + return {}; + } + auto identifier = consume_identifier_reference().value(); if (!match(TokenType::Colon)) return {}; @@ -549,14 +554,37 @@ RefPtr<Statement> Parser::try_parse_labelled_statement() if (!match_statement()) return {}; + + if (match(TokenType::Function) && (allow_function == AllowLabelledFunction::No || m_state.strict_mode)) { + syntax_error("Not allowed to declare a function here"); + return {}; + } + + if (m_state.labels_in_scope.contains(identifier)) + syntax_error(String::formatted("Label '{}' has already been declared", identifier)); m_state.labels_in_scope.set(identifier); - auto statement = parse_statement(); + + RefPtr<Statement> labelled_statement; + + if (match(TokenType::Function)) { + auto function_declaration = parse_function_node<FunctionDeclaration>(); + m_state.current_scope->function_declarations.append(function_declaration); + auto hoisting_target = m_state.current_scope->get_current_function_scope(); + hoisting_target->hoisted_function_declarations.append({ function_declaration, *m_state.current_scope }); + if (function_declaration->kind() == FunctionKind::Generator) + syntax_error("Generator functions cannot be defined in labelled statements"); + + labelled_statement = move(function_declaration); + } else { + labelled_statement = parse_statement(); + } + m_state.labels_in_scope.remove(identifier); - statement->set_label(identifier); + labelled_statement->set_label(identifier); state_rollback_guard.disarm(); discard_saved_state(); - return statement; + return labelled_statement.release_nonnull(); } RefPtr<MetaProperty> Parser::try_parse_new_target_expression() @@ -1632,7 +1660,7 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement(bool& is_strict, boo block->append(parse_declaration()); parsing_directives = false; } else if (match_statement()) { - auto statement = parse_statement(); + auto statement = parse_statement(AllowLabelledFunction::Yes); block->append(statement); if (statement_is_use_strict_directive(statement)) { if (parsing_directives) { diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 121408dca7..8157e430a7 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -50,7 +50,13 @@ public: }; NonnullRefPtr<Declaration> parse_declaration(); - NonnullRefPtr<Statement> parse_statement(); + + enum class AllowLabelledFunction { + No, + Yes + }; + + NonnullRefPtr<Statement> parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No); NonnullRefPtr<BlockStatement> parse_block_statement(); NonnullRefPtr<BlockStatement> parse_block_statement(bool& is_strict, bool error_on_binding = false); NonnullRefPtr<ReturnStatement> parse_return_statement(); @@ -91,7 +97,7 @@ public: NonnullRefPtr<ExportStatement> parse_export_statement(Program& program); RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens); - RefPtr<Statement> try_parse_labelled_statement(); + RefPtr<Statement> try_parse_labelled_statement(AllowLabelledFunction allow_function); RefPtr<MetaProperty> try_parse_new_target_expression(); struct Error { |