mirror of
https://github.com/RGBCube/serenity
synced 2025-05-17 19:15:08 +00:00
LibJS: break or continue with nonexistent label is a syntax error
This commit is contained in:
parent
67f2301150
commit
e8da5f99b1
4 changed files with 32 additions and 5 deletions
|
@ -393,6 +393,11 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
|
||||||
if (function_length == -1)
|
if (function_length == -1)
|
||||||
function_length = parameters.size();
|
function_length = parameters.size();
|
||||||
|
|
||||||
|
auto old_labels_in_scope = move(m_parser_state.m_labels_in_scope);
|
||||||
|
ScopeGuard guard([&]() {
|
||||||
|
m_parser_state.m_labels_in_scope = move(old_labels_in_scope);
|
||||||
|
});
|
||||||
|
|
||||||
bool is_strict = false;
|
bool is_strict = false;
|
||||||
|
|
||||||
auto function_body_result = [&]() -> RefPtr<BlockStatement> {
|
auto function_body_result = [&]() -> RefPtr<BlockStatement> {
|
||||||
|
@ -440,7 +445,9 @@ RefPtr<Statement> Parser::try_parse_labelled_statement()
|
||||||
|
|
||||||
if (!match_statement())
|
if (!match_statement())
|
||||||
return {};
|
return {};
|
||||||
|
m_parser_state.m_labels_in_scope.set(identifier);
|
||||||
auto statement = parse_statement();
|
auto statement = parse_statement();
|
||||||
|
m_parser_state.m_labels_in_scope.remove(identifier);
|
||||||
|
|
||||||
statement->set_label(identifier);
|
statement->set_label(identifier);
|
||||||
state_rollback_guard.disarm();
|
state_rollback_guard.disarm();
|
||||||
|
@ -1318,8 +1325,13 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_funct
|
||||||
if (function_length == -1)
|
if (function_length == -1)
|
||||||
function_length = parameters.size();
|
function_length = parameters.size();
|
||||||
|
|
||||||
bool is_strict = false;
|
|
||||||
TemporaryChange change(m_parser_state.m_in_function_context, true);
|
TemporaryChange change(m_parser_state.m_in_function_context, true);
|
||||||
|
auto old_labels_in_scope = move(m_parser_state.m_labels_in_scope);
|
||||||
|
ScopeGuard guard([&]() {
|
||||||
|
m_parser_state.m_labels_in_scope = move(old_labels_in_scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
bool is_strict = false;
|
||||||
auto body = parse_block_statement(is_strict);
|
auto body = parse_block_statement(is_strict);
|
||||||
body->add_variables(m_parser_state.m_var_scopes.last());
|
body->add_variables(m_parser_state.m_var_scopes.last());
|
||||||
body->add_functions(m_parser_state.m_function_scopes.last());
|
body->add_functions(m_parser_state.m_function_scopes.last());
|
||||||
|
@ -1395,8 +1407,11 @@ NonnullRefPtr<BreakStatement> Parser::parse_break_statement()
|
||||||
if (match(TokenType::Semicolon)) {
|
if (match(TokenType::Semicolon)) {
|
||||||
consume();
|
consume();
|
||||||
} else {
|
} else {
|
||||||
if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n'))
|
if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) {
|
||||||
target_label = consume().value();
|
target_label = consume().value();
|
||||||
|
if (!m_parser_state.m_labels_in_scope.contains(target_label))
|
||||||
|
syntax_error(String::formatted("Label '{}' not found", target_label));
|
||||||
|
}
|
||||||
consume_or_insert_semicolon();
|
consume_or_insert_semicolon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,8 +1432,11 @@ NonnullRefPtr<ContinueStatement> Parser::parse_continue_statement()
|
||||||
consume();
|
consume();
|
||||||
return create_ast_node<ContinueStatement>(target_label);
|
return create_ast_node<ContinueStatement>(target_label);
|
||||||
}
|
}
|
||||||
if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n'))
|
if (match(TokenType::Identifier) && !m_parser_state.m_current_token.trivia().contains('\n')) {
|
||||||
target_label = consume().value();
|
target_label = consume().value();
|
||||||
|
if (!m_parser_state.m_labels_in_scope.contains(target_label))
|
||||||
|
syntax_error(String::formatted("Label '{}' not found", target_label));
|
||||||
|
}
|
||||||
consume_or_insert_semicolon();
|
consume_or_insert_semicolon();
|
||||||
return create_ast_node<ContinueStatement>(target_label);
|
return create_ast_node<ContinueStatement>(target_label);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/HashTable.h>
|
||||||
#include <AK/NonnullRefPtr.h>
|
#include <AK/NonnullRefPtr.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
|
@ -155,6 +156,7 @@ private:
|
||||||
Vector<NonnullRefPtrVector<VariableDeclaration>> m_let_scopes;
|
Vector<NonnullRefPtrVector<VariableDeclaration>> m_let_scopes;
|
||||||
Vector<NonnullRefPtrVector<FunctionDeclaration>> m_function_scopes;
|
Vector<NonnullRefPtrVector<FunctionDeclaration>> m_function_scopes;
|
||||||
UseStrictDirectiveState m_use_strict_directive { UseStrictDirectiveState::None };
|
UseStrictDirectiveState m_use_strict_directive { UseStrictDirectiveState::None };
|
||||||
|
HashTable<StringView> m_labels_in_scope;
|
||||||
bool m_strict_mode { false };
|
bool m_strict_mode { false };
|
||||||
bool m_allow_super_property_lookup { false };
|
bool m_allow_super_property_lookup { false };
|
||||||
bool m_allow_super_constructor_call { false };
|
bool m_allow_super_constructor_call { false };
|
||||||
|
|
|
@ -2,8 +2,7 @@ test("'break' syntax errors", () => {
|
||||||
expect("break").not.toEval();
|
expect("break").not.toEval();
|
||||||
expect("break label").not.toEval();
|
expect("break label").not.toEval();
|
||||||
expect("{ break }").not.toEval();
|
expect("{ break }").not.toEval();
|
||||||
// FIXME: Parser does not throw error on nonexistent label
|
expect("{ break label }").not.toEval();
|
||||||
// expect("{ break label }.not.toEval();
|
|
||||||
expect("label: { break label }").toEval();
|
expect("label: { break label }").toEval();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -37,3 +37,11 @@ test("labeled for loop with continue", () => {
|
||||||
}
|
}
|
||||||
expect(counter).toBe(6);
|
expect(counter).toBe(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("invalid label across scope", () => {
|
||||||
|
expect(`
|
||||||
|
label: {
|
||||||
|
(() => { break label; });
|
||||||
|
}
|
||||||
|
`).not.toEval();
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue