1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:27:44 +00:00

LibJS: Add using declaration support, RAII like operation in js

In this patch only top level and not the more complicated for loop using
statements are supported. Also, as noted in the latest meeting of tc39
async parts of the spec are not stage 3 thus not included.
This commit is contained in:
davidot 2022-12-20 22:09:57 +01:00 committed by Linus Groh
parent 2c87ff2218
commit 541637e15a
15 changed files with 861 additions and 68 deletions

View file

@ -253,6 +253,11 @@ public:
return m_contains_await_expression;
}
bool can_have_using_declaration() const
{
return m_scope_level != ScopeLevel::ScriptTopLevel;
}
private:
void throw_identifier_declared(DeprecatedFlyString const& name, NonnullRefPtr<Declaration> const& declaration)
{
@ -597,6 +602,14 @@ NonnullRefPtr<Declaration> Parser::parse_declaration()
case TokenType::Let:
case TokenType::Const:
return parse_variable_declaration();
case TokenType::Identifier:
if (m_state.current_token.original_value() == "using"sv) {
if (!m_state.current_scope_pusher->can_have_using_declaration())
syntax_error("'using' not allowed outside of block, for loop or function");
return parse_using_declaration();
}
[[fallthrough]];
default:
expected("declaration");
consume();
@ -2457,7 +2470,7 @@ NonnullRefPtr<ReturnStatement> Parser::parse_return_statement()
void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction allow_labelled_functions)
{
while (!done()) {
if (match_declaration()) {
if (match_declaration(AllowUsingDeclaration::Yes)) {
auto declaration = parse_declaration();
VERIFY(m_state.current_scope_pusher);
m_state.current_scope_pusher->add_declaration(declaration);
@ -2949,7 +2962,37 @@ RefPtr<BindingPattern> Parser::parse_binding_pattern(Parser::AllowDuplicates all
return pattern;
}
NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_loop_variable_declaration)
RefPtr<Identifier> Parser::parse_lexical_binding()
{
auto binding_start = push_start();
if (match_identifier()) {
auto name = consume_identifier().DeprecatedFlyString_value();
return create_ast_node<Identifier>(
{ m_source_code, binding_start.position(), position() },
name);
}
if (!m_state.in_generator_function_context && match(TokenType::Yield)) {
if (m_state.strict_mode)
syntax_error("Identifier must not be a reserved word in strict mode ('yield')");
return create_ast_node<Identifier>(
{ m_source_code, binding_start.position(), position() },
consume().DeprecatedFlyString_value());
}
if (!m_state.await_expression_is_valid && match(TokenType::Async)) {
if (m_program_type == Program::Type::Module)
syntax_error("Identifier must not be a reserved word in modules ('async')");
return create_ast_node<Identifier>(
{ m_source_code, binding_start.position(), position() },
consume().DeprecatedFlyString_value());
}
return {};
}
NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(IsForLoopVariableDeclaration is_for_loop_variable_declaration)
{
auto rule_start = push_start();
DeclarationKind declaration_kind;
@ -2972,38 +3015,21 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l
NonnullRefPtrVector<VariableDeclarator> declarations;
for (;;) {
Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<BindingPattern>, Empty> target {};
if (match_identifier()) {
auto identifier_start = push_start();
auto name = consume_identifier().DeprecatedFlyString_value();
target = create_ast_node<Identifier>(
{ m_source_code, rule_start.position(), position() },
name);
check_identifier_name_for_assignment_validity(name);
if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const) && name == "let"sv)
syntax_error("Lexical binding may not be called 'let'");
} else if (auto pattern = parse_binding_pattern(declaration_kind != DeclarationKind::Var ? AllowDuplicates::No : AllowDuplicates::Yes, AllowMemberExpressions::No)) {
target = pattern.release_nonnull();
if (auto pattern = parse_binding_pattern(declaration_kind != DeclarationKind::Var ? AllowDuplicates::No : AllowDuplicates::Yes, AllowMemberExpressions::No)) {
if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const)) {
target.get<NonnullRefPtr<BindingPattern>>()->for_each_bound_name([this](auto& name) {
pattern->for_each_bound_name([this](auto& name) {
if (name == "let"sv)
syntax_error("Lexical binding may not be called 'let'");
});
}
} else if (!m_state.in_generator_function_context && match(TokenType::Yield)) {
if (m_state.strict_mode)
syntax_error("Identifier must not be a reserved word in strict mode ('yield')");
target = create_ast_node<Identifier>(
{ m_source_code, rule_start.position(), position() },
consume().DeprecatedFlyString_value());
} else if (!m_state.await_expression_is_valid && match(TokenType::Async)) {
if (m_program_type == Program::Type::Module)
syntax_error("Identifier must not be a reserved word in modules ('async')");
target = pattern.release_nonnull();
} else if (auto lexical_binding = parse_lexical_binding()) {
check_identifier_name_for_assignment_validity(lexical_binding->string());
if ((declaration_kind == DeclarationKind::Let || declaration_kind == DeclarationKind::Const) && lexical_binding->string() == "let"sv)
syntax_error("Lexical binding may not be called 'let'");
target = create_ast_node<Identifier>(
{ m_source_code, rule_start.position(), position() },
consume().DeprecatedFlyString_value());
target = lexical_binding.release_nonnull();
}
if (target.has<Empty>()) {
@ -3020,13 +3046,13 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l
consume();
// In a for loop 'in' can be ambiguous so we do not allow it
// 14.7.4 The for Statement, https://tc39.es/ecma262/#prod-ForStatement and 14.7.5 The for-in, for-of, and for-await-of Statements, https://tc39.es/ecma262/#prod-ForInOfStatement
if (for_loop_variable_declaration)
if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::Yes)
init = parse_expression(2, Associativity::Right, { TokenType::In });
else
init = parse_expression(2);
} else if (!for_loop_variable_declaration && declaration_kind == DeclarationKind::Const) {
} else if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::No && declaration_kind == DeclarationKind::Const) {
syntax_error("Missing initializer in 'const' variable declaration");
} else if (!for_loop_variable_declaration && target.has<NonnullRefPtr<BindingPattern>>()) {
} else if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::No && target.has<NonnullRefPtr<BindingPattern>>()) {
syntax_error("Missing initializer in destructuring assignment");
}
@ -3041,7 +3067,7 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l
}
break;
}
if (!for_loop_variable_declaration)
if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::No)
consume_or_insert_semicolon();
declarations.shrink_to_fit();
@ -3050,6 +3076,55 @@ NonnullRefPtr<VariableDeclaration> Parser::parse_variable_declaration(bool for_l
return declaration;
}
NonnullRefPtr<UsingDeclaration> Parser::parse_using_declaration(IsForLoopVariableDeclaration is_for_loop_variable_declaration)
{
// using [no LineTerminator here] BindingList[?In, ?Yield, ?Await, +Using] ;
auto rule_start = push_start();
VERIFY(m_state.current_token.original_value() == "using"sv);
consume(TokenType::Identifier);
VERIFY(!m_state.current_token.trivia_contains_line_terminator());
NonnullRefPtrVector<VariableDeclarator> declarations;
for (;;) {
auto lexical_binding = parse_lexical_binding();
if (!lexical_binding) {
expected("lexical binding");
break;
}
check_identifier_name_for_assignment_validity(lexical_binding->string());
if (lexical_binding->string() == "let"sv)
syntax_error("Lexical binding may not be called 'let'");
RefPtr<Expression> initializer;
if (match(TokenType::Equals)) {
consume();
if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::Yes)
initializer = parse_expression(2, Associativity::Right, { TokenType::In });
else
initializer = parse_expression(2);
} else if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::No) {
consume(TokenType::Equals);
}
declarations.append(create_ast_node<VariableDeclarator>(
{ m_source_code, rule_start.position(), position() },
lexical_binding.release_nonnull(),
move(initializer)));
if (match(TokenType::Comma)) {
consume();
continue;
}
break;
}
if (is_for_loop_variable_declaration == IsForLoopVariableDeclaration::No)
consume_or_insert_semicolon();
return create_ast_node<UsingDeclaration>({ m_source_code, rule_start.position(), position() }, move(declarations));
}
NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement()
{
auto rule_start = push_start();
@ -3499,7 +3574,7 @@ NonnullRefPtr<Statement> Parser::parse_for_statement()
RefPtr<ASTNode> init;
if (!match(TokenType::Semicolon)) {
if (match_variable_declaration()) {
auto declaration = parse_variable_declaration(true);
auto declaration = parse_variable_declaration(IsForLoopVariableDeclaration::Yes);
if (declaration->declaration_kind() == DeclarationKind::Var) {
m_state.current_scope_pusher->add_declaration(declaration);
} else {
@ -3763,7 +3838,7 @@ bool Parser::match_export_or_import() const
|| type == TokenType::Import;
}
bool Parser::match_declaration() const
bool Parser::match_declaration(AllowUsingDeclaration allow_using) const
{
auto type = m_state.current_token.type();
@ -3776,6 +3851,9 @@ bool Parser::match_declaration() const
return lookahead_token.type() == TokenType::Function && !lookahead_token.trivia_contains_line_terminator();
}
if (allow_using == AllowUsingDeclaration::Yes && type == TokenType::Identifier && m_state.current_token.original_value() == "using"sv)
return try_match_using_declaration();
return type == TokenType::Function
|| type == TokenType::Class
|| type == TokenType::Const
@ -3810,6 +3888,18 @@ bool Parser::try_match_let_declaration() const
return false;
}
bool Parser::try_match_using_declaration() const
{
VERIFY(m_state.current_token.type() == TokenType::Identifier);
VERIFY(m_state.current_token.original_value() == "using"sv);
auto token_after = next_token();
if (token_after.trivia_contains_line_terminator())
return false;
return token_after.is_identifier_name();
}
bool Parser::match_variable_declaration() const
{
auto type = m_state.current_token.type();