mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:57:35 +00:00
LibJS: Allow labelled functions in certain contexts
Also disallow duplicated labels.
This commit is contained in:
parent
4cc95ae39d
commit
b16c02d6b4
2 changed files with 45 additions and 11 deletions
|
@ -298,7 +298,7 @@ NonnullRefPtr<Program> Parser::parse_program(bool starts_in_strict_mode)
|
||||||
program->append(parse_declaration());
|
program->append(parse_declaration());
|
||||||
parsing_directives = false;
|
parsing_directives = false;
|
||||||
} else if (match_statement()) {
|
} else if (match_statement()) {
|
||||||
auto statement = parse_statement();
|
auto statement = parse_statement(AllowLabelledFunction::Yes);
|
||||||
program->append(statement);
|
program->append(statement);
|
||||||
if (statement_is_use_strict_directive(statement)) {
|
if (statement_is_use_strict_directive(statement)) {
|
||||||
if (parsing_directives) {
|
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();
|
auto rule_start = push_start();
|
||||||
switch (m_state.current_token.type()) {
|
switch (m_state.current_token.type()) {
|
||||||
|
@ -401,8 +401,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<EmptyStatement>({ m_state.current_token.filename(), rule_start.position(), position() });
|
return create_ast_node<EmptyStatement>({ m_state.current_token.filename(), rule_start.position(), position() });
|
||||||
default:
|
default:
|
||||||
if (match(TokenType::Identifier)) {
|
if (match_identifier_name()) {
|
||||||
auto result = try_parse_labelled_statement();
|
auto result = try_parse_labelled_statement(allow_labelled_function);
|
||||||
if (!result.is_null())
|
if (!result.is_null())
|
||||||
return result.release_nonnull();
|
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);
|
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();
|
save_state();
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
|
@ -542,6 +542,11 @@ RefPtr<Statement> Parser::try_parse_labelled_statement()
|
||||||
load_state();
|
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();
|
auto identifier = consume_identifier_reference().value();
|
||||||
if (!match(TokenType::Colon))
|
if (!match(TokenType::Colon))
|
||||||
return {};
|
return {};
|
||||||
|
@ -549,14 +554,37 @@ RefPtr<Statement> Parser::try_parse_labelled_statement()
|
||||||
|
|
||||||
if (!match_statement())
|
if (!match_statement())
|
||||||
return {};
|
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);
|
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);
|
m_state.labels_in_scope.remove(identifier);
|
||||||
|
|
||||||
statement->set_label(identifier);
|
labelled_statement->set_label(identifier);
|
||||||
state_rollback_guard.disarm();
|
state_rollback_guard.disarm();
|
||||||
discard_saved_state();
|
discard_saved_state();
|
||||||
return statement;
|
return labelled_statement.release_nonnull();
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<MetaProperty> Parser::try_parse_new_target_expression()
|
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());
|
block->append(parse_declaration());
|
||||||
parsing_directives = false;
|
parsing_directives = false;
|
||||||
} else if (match_statement()) {
|
} else if (match_statement()) {
|
||||||
auto statement = parse_statement();
|
auto statement = parse_statement(AllowLabelledFunction::Yes);
|
||||||
block->append(statement);
|
block->append(statement);
|
||||||
if (statement_is_use_strict_directive(statement)) {
|
if (statement_is_use_strict_directive(statement)) {
|
||||||
if (parsing_directives) {
|
if (parsing_directives) {
|
||||||
|
|
|
@ -50,7 +50,13 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
NonnullRefPtr<Declaration> parse_declaration();
|
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();
|
||||||
NonnullRefPtr<BlockStatement> parse_block_statement(bool& is_strict, bool error_on_binding = false);
|
NonnullRefPtr<BlockStatement> parse_block_statement(bool& is_strict, bool error_on_binding = false);
|
||||||
NonnullRefPtr<ReturnStatement> parse_return_statement();
|
NonnullRefPtr<ReturnStatement> parse_return_statement();
|
||||||
|
@ -91,7 +97,7 @@ public:
|
||||||
NonnullRefPtr<ExportStatement> parse_export_statement(Program& program);
|
NonnullRefPtr<ExportStatement> parse_export_statement(Program& program);
|
||||||
|
|
||||||
RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens);
|
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();
|
RefPtr<MetaProperty> try_parse_new_target_expression();
|
||||||
|
|
||||||
struct Error {
|
struct Error {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue