1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 04:38:11 +00:00

LibJS: Hoist function declarations

This patch adds function declaration hoisting. The mechanism
is similar to var hoisting. Hoisted function declarations are to be put
before the hoisted var declarations, hence they have to be treated
separately.
This commit is contained in:
Marcin Gasperowicz 2020-06-04 14:48:36 +02:00 committed by Andreas Kling
parent e162b59a5e
commit 2579d0bf55
6 changed files with 73 additions and 15 deletions

View file

@ -37,6 +37,7 @@ public:
enum Type {
Var = 1,
Let = 2,
Function = 3,
};
ScopePusher(Parser& parser, unsigned mask)
@ -47,6 +48,8 @@ public:
m_parser.m_parser_state.m_var_scopes.append(NonnullRefPtrVector<VariableDeclaration>());
if (m_mask & Let)
m_parser.m_parser_state.m_let_scopes.append(NonnullRefPtrVector<VariableDeclaration>());
if (m_mask & Function)
m_parser.m_parser_state.m_function_scopes.append(NonnullRefPtrVector<FunctionDeclaration>());
}
~ScopePusher()
@ -55,6 +58,8 @@ public:
m_parser.m_parser_state.m_var_scopes.take_last();
if (m_mask & Let)
m_parser.m_parser_state.m_let_scopes.take_last();
if (m_mask & Function)
m_parser.m_parser_state.m_function_scopes.take_last();
}
Parser& m_parser;
@ -204,7 +209,7 @@ Associativity Parser::operator_associativity(TokenType type) const
NonnullRefPtr<Program> Parser::parse_program()
{
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Let);
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Let | ScopePusher::Function);
auto program = adopt(*new Program);
bool first = true;
@ -228,6 +233,7 @@ NonnullRefPtr<Program> Parser::parse_program()
if (m_parser_state.m_var_scopes.size() == 1) {
program->add_variables(m_parser_state.m_var_scopes.last());
program->add_variables(m_parser_state.m_let_scopes.last());
program->add_functions(m_parser_state.m_function_scopes.last());
} else {
syntax_error("Unclosed scope");
}
@ -238,8 +244,11 @@ NonnullRefPtr<Statement> Parser::parse_statement()
{
auto statement = [this]() -> NonnullRefPtr<Statement> {
switch (m_parser_state.m_current_token.type()) {
case TokenType::Function:
return parse_function_node<FunctionDeclaration>();
case TokenType::Function: {
auto declaration = parse_function_node<FunctionDeclaration>();
m_parser_state.m_function_scopes.last().append(declaration);
return declaration;
}
case TokenType::CurlyOpen:
return parse_block_statement();
case TokenType::Return:
@ -590,8 +599,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
syntax_error(
"Expected '(' for object getter or setter property",
m_parser_state.m_current_token.line_number(),
m_parser_state.m_current_token.line_column()
);
m_parser_state.m_current_token.line_column());
skip_to_next_property();
continue;
}
@ -606,8 +614,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
syntax_error(
"Object getter property must have no arguments",
m_parser_state.m_current_token.line_number(),
m_parser_state.m_current_token.line_column()
);
m_parser_state.m_current_token.line_column());
skip_to_next_property();
continue;
}
@ -615,8 +622,7 @@ NonnullRefPtr<ObjectExpression> Parser::parse_object_expression()
syntax_error(
"Object setter property must have one argument",
m_parser_state.m_current_token.line_number(),
m_parser_state.m_current_token.line_column()
);
m_parser_state.m_current_token.line_column());
skip_to_next_property();
continue;
}
@ -1059,13 +1065,14 @@ NonnullRefPtr<BlockStatement> Parser::parse_block_statement()
m_parser_state.m_strict_mode = initial_strict_mode_state;
consume(TokenType::CurlyClose);
block->add_variables(m_parser_state.m_let_scopes.last());
block->add_functions(m_parser_state.m_function_scopes.last());
return block;
}
template<typename FunctionNodeType>
NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_function_and_name)
{
ScopePusher scope(*this, ScopePusher::Var);
ScopePusher scope(*this, ScopePusher::Var | ScopePusher::Function);
if (check_for_function_and_name)
consume(TokenType::Function);
@ -1109,6 +1116,7 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(bool check_for_funct
auto body = parse_block_statement();
body->add_variables(m_parser_state.m_var_scopes.last());
body->add_functions(m_parser_state.m_function_scopes.last());
return create_ast_node<FunctionNodeType>(name, move(body), move(parameters), function_length, NonnullRefPtrVector<VariableDeclaration>());
}