diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 696c36cb84a..7bf76f91dae 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -2679,7 +2679,11 @@ Completion Identifier::execute(Interpreter& interpreter) const void Identifier::dump(int indent) const { print_indent(indent); - outln("Identifier \"{}\"", m_string); + if (is_local()) { + outln("Identifier \"{}\" is_local=(true) index=({})", m_string, m_local_variable_index); + } else { + outln("Identifier \"{}\"", m_string); + } } Completion PrivateIdentifier::execute(Interpreter&) const @@ -4435,6 +4439,16 @@ ThrowCompletionOr ScopeNode::for_each_var_function_declaration_in_reverse_ return {}; } +ThrowCompletionOr ScopeNode::for_each_lexical_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback&& callback) const +{ + for (ssize_t i = m_lexical_declarations.size() - 1; i >= 0; i--) { + auto& declaration = m_lexical_declarations[i]; + if (is(declaration)) + TRY(callback(static_cast(*declaration))); + } + return {}; +} + ThrowCompletionOr ScopeNode::for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback&& callback) const { for (auto& declaration : m_var_declarations) { diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 490cd125608..127cb7ce9bb 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -312,12 +312,21 @@ public: ThrowCompletionOr for_each_var_declared_name(ThrowCompletionOrVoidCallback&& callback) const; ThrowCompletionOr for_each_var_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback&& callback) const; + ThrowCompletionOr for_each_lexical_function_declaration_in_reverse_order(ThrowCompletionOrVoidCallback&& callback) const; ThrowCompletionOr for_each_var_scoped_variable_declaration(ThrowCompletionOrVoidCallback&& callback) const; void block_declaration_instantiation(VM&, Environment*) const; ThrowCompletionOr for_each_function_hoistable_with_annexB_extension(ThrowCompletionOrVoidCallback&& callback) const; + Vector const& local_variables_names() const { return m_local_variables_names; } + size_t add_local_variable(DeprecatedFlyString name) + { + auto index = m_local_variables_names.size(); + m_local_variables_names.append(name); + return index; + } + protected: explicit ScopeNode(SourceRange source_range) : Statement(source_range) @@ -332,6 +341,8 @@ private: Vector> m_var_declarations; Vector> m_functions_hoistable_with_annexB_extension; + + Vector m_local_variables_names; }; // ImportEntry Record, https://tc39.es/ecma262/#table-importentry-record-fields @@ -659,6 +670,7 @@ public: Statement const& body() const { return *m_body; } Vector const& parameters() const { return m_parameters; }; i32 function_length() const { return m_function_length; } + Vector const& local_variables_names() const { return m_local_variables_names; } bool is_strict_mode() const { return m_is_strict_mode; } bool might_need_arguments_object() const { return m_might_need_arguments_object; } bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; } @@ -666,7 +678,7 @@ public: FunctionKind kind() const { return m_kind; } protected: - FunctionNode(DeprecatedFlyString name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function) + FunctionNode(RefPtr name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function, Vector local_variables_names) : m_name(move(name)) , m_source_text(move(source_text)) , m_body(move(body)) @@ -677,6 +689,7 @@ protected: , m_might_need_arguments_object(might_need_arguments_object) , m_contains_direct_call_to_eval(contains_direct_call_to_eval) , m_is_arrow_function(is_arrow_function) + , m_local_variables_names(local_variables_names) { if (m_is_arrow_function) VERIFY(!m_might_need_arguments_object); @@ -695,6 +708,8 @@ private: bool m_might_need_arguments_object : 1 { false }; bool m_contains_direct_call_to_eval : 1 { false }; bool m_is_arrow_function : 1 { false }; + + Vector m_local_variables_names; }; class FunctionDeclaration final @@ -703,9 +718,9 @@ class FunctionDeclaration final public: static bool must_have_name() { return true; } - FunctionDeclaration(SourceRange source_range, DeprecatedFlyString const& name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval) + FunctionDeclaration(SourceRange source_range, RefPtr name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names) : Declaration(source_range) - , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false) + , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false, move(local_variables_names)) { } @@ -729,9 +744,9 @@ class FunctionExpression final public: static bool must_have_name() { return false; } - FunctionExpression(SourceRange source_range, DeprecatedFlyString const& name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function = false) + FunctionExpression(SourceRange source_range, RefPtr name, DeprecatedString source_text, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, Vector local_variables_names, bool is_arrow_function = false) : Expression(source_range) - , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function) + , FunctionNode(name, move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function, move(local_variables_names)) { } @@ -1254,6 +1269,14 @@ public: DeprecatedFlyString const& string() const { return m_string; } + bool is_local() const { return m_local_variable_index.has_value(); } + size_t local_variable_index() const + { + VERIFY(m_local_variable_index.has_value()); + return m_local_variable_index.value(); + } + void set_local_variable_index(size_t index) { m_local_variable_index = index; } + virtual Completion execute(Interpreter&) const override; virtual void dump(int indent) const override; virtual ThrowCompletionOr to_reference(Interpreter&) const override; @@ -1264,6 +1287,8 @@ private: DeprecatedFlyString m_string; mutable EnvironmentCoordinate m_cached_environment_coordinate; + + Optional m_local_variable_index; }; class PrivateIdentifier final : public Expression { diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 34a3e401f24..d9ab8aa10ff 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -35,10 +35,23 @@ class ScopePusher { StaticInitTopLevel }; + enum class ScopeType { + Function, + Program, + Block, + ForLoop, + With, + Catch, + ClassStaticInit, + ClassField, + ClassDeclaration, + }; + private: - ScopePusher(Parser& parser, ScopeNode* node, ScopeLevel scope_level) + ScopePusher(Parser& parser, ScopeNode* node, ScopeLevel scope_level, ScopeType type) : m_parser(parser) , m_scope_level(scope_level) + , m_type(type) { m_parent_scope = exchange(m_parser.m_state.current_scope_pusher, this); VERIFY(node || (m_parent_scope && scope_level == ScopeLevel::NotTopLevel)); @@ -62,7 +75,7 @@ private: public: static ScopePusher function_scope(Parser& parser, FunctionBody& function_body, Vector const& parameters) { - ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel); + ScopePusher scope_pusher(parser, &function_body, ScopeLevel::FunctionTopLevel, ScopeType::Function); scope_pusher.m_function_parameters = parameters; for (auto& parameter : parameters) { parameter.binding.visit( @@ -81,52 +94,61 @@ public: static ScopePusher program_scope(Parser& parser, Program& program) { - return ScopePusher(parser, &program, program.type() == Program::Type::Script ? ScopeLevel::ScriptTopLevel : ScopeLevel::ModuleTopLevel); + return ScopePusher(parser, &program, program.type() == Program::Type::Script ? ScopeLevel::ScriptTopLevel : ScopeLevel::ModuleTopLevel, ScopeType::Program); } static ScopePusher block_scope(Parser& parser, ScopeNode& node) { - return ScopePusher(parser, &node, ScopeLevel::NotTopLevel); + return ScopePusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::Block); } - static ScopePusher for_loop_scope(Parser& parser, RefPtr const& init) + static ScopePusher for_loop_scope(Parser& parser, ScopeNode& node) { - ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel); - if (init && is(*init)) { - auto& variable_declaration = static_cast(*init); - if (variable_declaration.declaration_kind() != DeclarationKind::Var) { - // NOTE: Nothing in the callback throws an exception. - MUST(variable_declaration.for_each_bound_name([&](auto const& name) { - scope_pusher.m_forbidden_var_names.set(name); - })); - } - } + return ScopePusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::ForLoop); + } + static ScopePusher with_scope(Parser& parser, ScopeNode& node) + { + ScopePusher scope_pusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::With); return scope_pusher; } static ScopePusher catch_scope(Parser& parser, RefPtr const& pattern, DeprecatedFlyString const& parameter) { - ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel); + ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel, ScopeType::Catch); if (pattern) { // NOTE: Nothing in the callback throws an exception. MUST(pattern->for_each_bound_name([&](auto const& name) { scope_pusher.m_forbidden_var_names.set(name); + scope_pusher.m_bound_names.set(name); })); } else if (!parameter.is_empty()) { scope_pusher.m_var_names.set(parameter); + scope_pusher.m_bound_names.set(parameter); } + return scope_pusher; } static ScopePusher static_init_block_scope(Parser& parser, ScopeNode& node) { - return ScopePusher(parser, &node, ScopeLevel::StaticInitTopLevel); + ScopePusher scope_pusher(parser, &node, ScopeLevel::StaticInitTopLevel, ScopeType::ClassStaticInit); + return scope_pusher; } - static ScopePusher class_field_scope(Parser& parser) + static ScopePusher class_field_scope(Parser& parser, ScopeNode& node) { - return ScopePusher(parser, nullptr, ScopeLevel::NotTopLevel); + ScopePusher scope_pusher(parser, &node, ScopeLevel::NotTopLevel, ScopeType::ClassField); + return scope_pusher; + } + + static ScopePusher class_declaration_scope(Parser& parser, RefPtr class_name) + { + ScopePusher scope_pusher(parser, nullptr, ScopeLevel::NotTopLevel, ScopeType::ClassDeclaration); + if (class_name) { + scope_pusher.m_bound_names.set(class_name->string()); + } + return scope_pusher; } void add_declaration(NonnullRefPtr declaration) @@ -176,7 +198,7 @@ public: } else { VERIFY(is(*declaration)); auto& function_declaration = static_cast(*declaration); - auto& function_name = function_declaration.name(); + auto function_name = function_declaration.name(); if (m_var_names.contains(function_name) || m_lexical_names.contains(function_name)) throw_identifier_declared(function_name, declaration); @@ -222,7 +244,11 @@ public: bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; } bool contains_access_to_arguments_object() const { return m_contains_access_to_arguments_object; } - void set_contains_direct_call_to_eval() { m_contains_direct_call_to_eval = true; } + void set_contains_direct_call_to_eval() + { + m_contains_direct_call_to_eval = true; + m_can_use_local_variables = false; + } void set_contains_access_to_arguments_object() { m_contains_access_to_arguments_object = true; } ~ScopePusher() @@ -241,12 +267,98 @@ public: } } + for (auto& it : m_identifier_groups) { + auto const& identifier_group_name = it.key; + auto& identifier_group = it.value; + + if (identifier_group_name == "arguments"sv) { + // NOTE: arguments is a special variable that should not be treated as a candidate to become local + continue; + } + + bool scope_has_declaration = false; + MUST(m_node->for_each_var_declared_name([&](auto const& name) { + if (m_function_parameters.has_value() && m_forbidden_lexical_names.contains(name)) + return; + if (name == identifier_group_name) + scope_has_declaration = true; + })); + MUST(m_node->for_each_lexically_declared_name([&](auto const& name) { + if (name == identifier_group_name) + scope_has_declaration = true; + })); + + bool function_declaration = false; + MUST(m_node->for_each_var_function_declaration_in_reverse_order([&](auto const& declaration) { + if (declaration.name() == identifier_group_name) + function_declaration = true; + })); + MUST(m_node->for_each_lexical_function_declaration_in_reverse_order([&](auto const& declaration) { + if (declaration.name() == identifier_group_name) + function_declaration = true; + })); + MUST(m_node->for_each_function_hoistable_with_annexB_extension([&](auto const& declaration) { + if (declaration.name() == identifier_group_name) + function_declaration = true; + })); + + if ((m_type == ScopeType::ClassDeclaration || m_type == ScopeType::Catch) && m_bound_names.contains(identifier_group_name)) { + // NOTE: Currently class names and catch section parameters are not considered to become local variables + // but this might be fixed in the future + continue; + } + + if (m_type == ScopeType::ClassDeclaration || m_type == ScopeType::Catch) { + // NOTE: Class declaration and catch scopes do not have own ScopeNode hence can't contain declaration of any variable + scope_has_declaration = false; + } + + if (scope_has_declaration) { + if (function_declaration) + continue; + + if (!identifier_group.captured_by_nested_function) { + auto function_scope = last_function_scope(); + if (!function_scope || !m_can_use_local_variables) { + continue; + } + + auto local_variable_index = function_scope->m_node->add_local_variable(identifier_group_name); + for (auto& identifier : identifier_group.identifiers) + identifier->set_local_variable_index(local_variable_index); + } + } else { + if (m_function_parameters.has_value() || m_type == ScopeType::ClassField || m_type == ScopeType::ClassStaticInit || m_type == ScopeType::With) { + // NOTE: Class fields and class static initialization sections implicitly create functions + identifier_group.captured_by_nested_function = true; + } + + if (m_parent_scope) { + if (auto maybe_parent_scope_identifier_group = m_parent_scope->m_identifier_groups.get(identifier_group_name); maybe_parent_scope_identifier_group.has_value()) { + maybe_parent_scope_identifier_group.value().identifiers.extend(identifier_group.identifiers); + if (identifier_group.captured_by_nested_function) + maybe_parent_scope_identifier_group.value().captured_by_nested_function = true; + } else { + m_parent_scope->m_identifier_groups.set(identifier_group_name, identifier_group); + } + } + } + } + if (m_parent_scope && !m_function_parameters.has_value()) { m_parent_scope->m_contains_access_to_arguments_object |= m_contains_access_to_arguments_object; m_parent_scope->m_contains_direct_call_to_eval |= m_contains_direct_call_to_eval; m_parent_scope->m_contains_await_expression |= m_contains_await_expression; } + if (m_parent_scope) { + if (m_contains_direct_call_to_eval) { + m_parent_scope->m_can_use_local_variables = false; + } else { + m_can_use_local_variables = m_parent_scope->m_can_use_local_variables && m_can_use_local_variables; + } + } + VERIFY(m_parser.m_state.current_scope_pusher == this); m_parser.m_state.current_scope_pusher = m_parent_scope; } @@ -266,6 +378,18 @@ public: return m_scope_level != ScopeLevel::ScriptTopLevel; } + void register_identifier(NonnullRefPtr id) + { + if (auto maybe_identifier_group = m_identifier_groups.get(id->string()); maybe_identifier_group.has_value()) { + maybe_identifier_group.value().identifiers.append(id); + } else { + m_identifier_groups.set(id->string(), IdentifierGroup { + .captured_by_nested_function = false, + .identifiers = { id }, + }); + } + } + private: void throw_identifier_declared(DeprecatedFlyString const& name, NonnullRefPtr const& declaration) { @@ -275,6 +399,7 @@ private: Parser& m_parser; ScopeNode* m_node { nullptr }; ScopeLevel m_scope_level { ScopeLevel::NotTopLevel }; + ScopeType m_type; ScopePusher* m_parent_scope { nullptr }; ScopePusher* m_top_level_scope { nullptr }; @@ -287,11 +412,20 @@ private: HashTable m_forbidden_var_names; Vector> m_functions_to_hoist; + HashTable m_bound_names; + + struct IdentifierGroup { + bool captured_by_nested_function { false }; + Vector> identifiers; + }; + HashMap m_identifier_groups; + Optional> m_function_parameters; bool m_contains_access_to_arguments_object { false }; bool m_contains_direct_call_to_eval { false }; bool m_contains_await_expression { false }; + bool m_can_use_local_variables { true }; }; class OperatorPrecedenceTable { @@ -860,6 +994,8 @@ RefPtr Parser::try_parse_arrow_function_expression(boo if (function_body_result.is_null()) return nullptr; + auto local_variables_names = function_body_result->local_variables_names(); + state_rollback_guard.disarm(); discard_saved_state(); auto body = function_body_result.release_nonnull(); @@ -878,9 +1014,9 @@ RefPtr Parser::try_parse_arrow_function_expression(boo auto function_end_offset = position().offset - m_state.current_token.trivia().length(); auto source_text = DeprecatedString { m_state.lexer.source().substring_view(function_start_offset, function_end_offset - function_start_offset) }; return create_ast_node( - { m_source_code, rule_start.position(), position() }, "", move(source_text), + { m_source_code, rule_start.position(), position() }, nullptr, move(source_text), move(body), move(parameters), function_length, function_kind, body->in_strict_mode(), - /* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true); + /* might_need_arguments_object */ false, contains_direct_call_to_eval, move(local_variables_names), /* is_arrow_function */ true); } RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) @@ -1071,14 +1207,13 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_ RefPtr constructor; HashTable found_private_names; - RefPtr class_name { nullptr }; + RefPtr class_name; if (expect_class_name || match_identifier() || match(TokenType::Yield) || match(TokenType::Await)) { - class_name = create_ast_node( - { m_source_code, rule_start.position(), position() }, - consume_identifier_reference().DeprecatedFlyString_value() - ); + class_name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume_identifier_reference().DeprecatedFlyString_value()); } + ScopePusher class_declaration_scope = ScopePusher::class_declaration_scope(*this, class_name); + if (class_name) check_identifier_name_for_assignment_validity(class_name->string(), true); if (m_state.in_class_static_init_block && class_name && class_name->string() == "await"sv) @@ -1337,7 +1472,8 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_ TemporaryChange super_property_access_rollback(m_state.allow_super_property_lookup, true); TemporaryChange field_initializer_rollback(m_state.in_class_field_initializer, true); - auto class_field_scope = ScopePusher::class_field_scope(*this); + auto class_scope_node = create_ast_node({ m_source_code, rule_start.position(), position() }); + auto class_field_scope = ScopePusher::class_field_scope(*this, *class_scope_node); initializer = parse_expression(2); contains_direct_call_to_eval = class_field_scope.contains_direct_call_to_eval(); } @@ -1370,14 +1506,14 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_ constructor_body->append(create_ast_node({ m_source_code, rule_start.position(), position() }, move(super_call))); constructor = create_ast_node( - { m_source_code, rule_start.position(), position() }, class_name ? class_name->string() : "", "", + { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), Vector { FunctionParameter { move(argument_name), nullptr, true } }, 0, FunctionKind::Normal, - /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); + /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false, /* local_variables_names */ Vector {}); } else { constructor = create_ast_node( - { m_source_code, rule_start.position(), position() }, class_name ? class_name->string() : "", "", + { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), Vector {}, 0, FunctionKind::Normal, - /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); + /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false, /* local_variables_names */ Vector {}); } } @@ -1759,7 +1895,7 @@ NonnullRefPtr Parser::parse_object_expression() property_key = parse_property_key(); } else { property_key = create_ast_node({ m_source_code, rule_start.position(), position() }, identifier.value()); - property_value = create_ast_node({ m_source_code, rule_start.position(), position() }, identifier.DeprecatedFlyString_value()); + property_value = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, identifier.DeprecatedFlyString_value()); } } else { property_key = parse_property_key(); @@ -2277,6 +2413,7 @@ RefPtr Parser::synthesize_binding_pattern(Expression const Lexer lexer { source, m_state.lexer.filename(), expression.source_range().start.line, expression.source_range().start.column }; Parser parser { lexer }; + parser.m_state.current_scope_pusher = m_state.current_scope_pusher; parser.m_state.strict_mode = m_state.strict_mode; parser.m_state.allow_super_property_lookup = m_state.allow_super_property_lookup; parser.m_state.allow_super_constructor_call = m_state.allow_super_constructor_call; @@ -2355,9 +2492,7 @@ NonnullRefPtr Parser::parse_identifier() auto token = consume_identifier(); if (m_state.in_class_field_initializer && token.value() == "arguments"sv) syntax_error("'arguments' is not allowed in class field initializer"); - return create_ast_node( - { m_source_code, identifier_start, position() }, - token.DeprecatedFlyString_value()); + return create_identifier_and_register_in_current_scope({ m_source_code, identifier_start, position() }, token.DeprecatedFlyString_value()); } Vector Parser::parse_arguments() @@ -2619,7 +2754,7 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O function_kind = FunctionKind::Async; else function_kind = FunctionKind::Normal; - DeprecatedFlyString name; + RefPtr name; if (parse_options & FunctionNodeParseOptions::CheckForFunctionAndName) { if (function_kind == FunctionKind::Normal && match(TokenType::Async) && !next_token().trivia_contains_line_terminator()) { function_kind = FunctionKind::Async; @@ -2633,20 +2768,29 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O parse_options |= FunctionNodeParseOptions::IsGeneratorFunction; } - if (parse_options & FunctionNodeParseOptions::HasDefaultExportName) - name = ExportStatement::local_name_for_default; - else if (FunctionNodeType::must_have_name() || match_identifier()) - name = consume_identifier().DeprecatedFlyString_value(); - else if (is_function_expression && (match(TokenType::Yield) || match(TokenType::Await))) - name = consume().DeprecatedFlyString_value(); + if (parse_options & FunctionNodeParseOptions::HasDefaultExportName) { + name = create_ast_node( + { m_source_code, rule_start.position(), position() }, + ExportStatement::local_name_for_default); + } else if (FunctionNodeType::must_have_name() || match_identifier()) { + name = create_ast_node( + { m_source_code, rule_start.position(), position() }, + consume_identifier().DeprecatedFlyString_value()); + } else if (is_function_expression && (match(TokenType::Yield) || match(TokenType::Await))) { + name = create_ast_node( + { m_source_code, rule_start.position(), position() }, + consume().DeprecatedFlyString_value()); + } - check_identifier_name_for_assignment_validity(name); + if (name) { + check_identifier_name_for_assignment_validity(name->string()); - if (function_kind == FunctionKind::AsyncGenerator && (name == "await"sv || name == "yield"sv)) - syntax_error(DeprecatedString::formatted("async generator function is not allowed to be called '{}'", name)); + if (function_kind == FunctionKind::AsyncGenerator && (name->string() == "await"sv || name->string() == "yield"sv)) + syntax_error(DeprecatedString::formatted("async generator function is not allowed to be called '{}'", name->string())); - if (m_state.in_class_static_init_block && name == "await"sv) - syntax_error("'await' is a reserved word"); + if (m_state.in_class_static_init_block && name->string() == "await"sv) + syntax_error("'await' is a reserved word"); + } } TemporaryChange class_static_initializer_rollback(m_state.in_class_static_init_block, false); TemporaryChange generator_change(m_state.in_generator_function_context, function_kind == FunctionKind::Generator || function_kind == FunctionKind::AsyncGenerator); @@ -2670,12 +2814,13 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O consume(TokenType::CurlyOpen); bool contains_direct_call_to_eval = false; auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval); + auto local_variables_names = body->local_variables_names(); consume(TokenType::CurlyClose); auto has_strict_directive = body->in_strict_mode(); - if (has_strict_directive) - check_identifier_name_for_assignment_validity(name, true); + if (has_strict_directive && name) + check_identifier_name_for_assignment_validity(name->string(), true); auto function_start_offset = rule_start.position().offset; auto function_end_offset = position().offset - m_state.current_token.trivia().length(); @@ -2684,7 +2829,8 @@ NonnullRefPtr Parser::parse_function_node(u16 parse_options, O { m_source_code, rule_start.position(), position() }, name, move(source_text), move(body), move(parameters), function_length, function_kind, has_strict_directive, m_state.function_might_need_arguments_object, - contains_direct_call_to_eval); + contains_direct_call_to_eval, + move(local_variables_names)); } Vector Parser::parse_formal_parameters(int& function_length, u16 parse_options) @@ -2842,20 +2988,13 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat if (match(TokenType::StringLiteral)) { auto token = consume(TokenType::StringLiteral); auto string_literal = parse_string_literal(token); - - name = create_ast_node( - { m_source_code, rule_start.position(), position() }, - string_literal->value()); + name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, string_literal->value()); } else if (match(TokenType::BigIntLiteral)) { auto string_value = consume().DeprecatedFlyString_value(); VERIFY(string_value.ends_with("n"sv)); - name = create_ast_node( - { m_source_code, rule_start.position(), position() }, - DeprecatedFlyString(string_value.view().substring_view(0, string_value.length() - 1))); + name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, DeprecatedFlyString(string_value.view().substring_view(0, string_value.length() - 1))); } else { - name = create_ast_node( - { m_source_code, rule_start.position(), position() }, - consume().DeprecatedFlyString_value()); + name = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume().DeprecatedFlyString_value()); } } else if (match(TokenType::BracketOpen)) { consume(); @@ -2891,10 +3030,7 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat return {}; alias = binding_pattern.release_nonnull(); } else if (match_identifier_name()) { - alias = create_ast_node( - { m_source_code, rule_start.position(), position() }, - consume().DeprecatedFlyString_value()); - + alias = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, consume().DeprecatedFlyString_value()); } else { expected("identifier or binding pattern"); return {}; @@ -2930,9 +3066,7 @@ RefPtr Parser::parse_binding_pattern(Parser::AllowDuplicat } else if (match_identifier_name()) { // BindingElement must always have an Empty name field auto identifier_name = consume_identifier().DeprecatedFlyString_value(); - alias = create_ast_node( - { m_source_code, rule_start.position(), position() }, - identifier_name); + alias = create_identifier_and_register_in_current_scope({ m_source_code, rule_start.position(), position() }, identifier_name); } else { expected("identifier or binding pattern"); return {}; @@ -2996,26 +3130,19 @@ RefPtr Parser::parse_lexical_binding() auto binding_start = push_start(); if (match_identifier()) { - auto name = consume_identifier().DeprecatedFlyString_value(); - return create_ast_node( - { m_source_code, binding_start.position(), position() }, - name); + return create_identifier_and_register_in_current_scope({ m_source_code, binding_start.position(), position() }, consume_identifier().DeprecatedFlyString_value()); } 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( - { m_source_code, binding_start.position(), position() }, - consume().DeprecatedFlyString_value()); + return create_identifier_and_register_in_current_scope({ 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( - { m_source_code, binding_start.position(), position() }, - consume().DeprecatedFlyString_value()); + return create_identifier_and_register_in_current_scope({ m_source_code, binding_start.position(), position() }, consume().DeprecatedFlyString_value()); } return {}; @@ -3433,6 +3560,9 @@ NonnullRefPtr Parser::parse_with_statement() consume(TokenType::ParenClose); + auto with_scope_node = create_ast_node({ m_source_code, rule_start.position(), position() }); + ScopePusher with_scope = ScopePusher::with_scope(*this, with_scope_node); + auto body = parse_statement(); return create_ast_node({ m_source_code, rule_start.position(), position() }, move(object), move(body)); } @@ -3573,6 +3703,9 @@ NonnullRefPtr Parser::parse_for_statement() auto rule_start = push_start(); auto is_await_loop = IsForAwaitLoop::No; + auto loop_scope_node = create_ast_node({ m_source_code, rule_start.position(), position() }); + ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, *loop_scope_node); + auto match_of = [&](Token const& token) { return token.type() == TokenType::Identifier && token.original_value() == "of"sv; }; @@ -3638,18 +3771,7 @@ NonnullRefPtr Parser::parse_for_statement() init = move(declaration); } else if (match_variable_declaration()) { auto declaration = parse_variable_declaration(IsForLoopVariableDeclaration::Yes); - if (declaration->declaration_kind() == DeclarationKind::Var) { - m_state.current_scope_pusher->add_declaration(declaration); - } else { - // This does not follow the normal declaration structure so we need additional checks. - HashTable bound_names; - // NOTE: Nothing in the callback throws an exception. - MUST(declaration->for_each_bound_name([&](auto const& name) { - if (bound_names.set(name) != AK::HashSetResult::InsertedNewEntry) - syntax_error(DeprecatedString::formatted("Identifier '{}' already declared in for loop initializer", name), declaration->source_range().start); - })); - } - + m_state.current_scope_pusher->add_declaration(declaration); if (match_for_in_of()) { if (declaration->declarations().size() > 1) syntax_error("Multiple declarations not allowed in for..in/of"); @@ -3697,7 +3819,7 @@ NonnullRefPtr Parser::parse_for_statement() TemporaryChange break_change(m_state.in_break_context, true); TemporaryChange continue_change(m_state.in_continue_context, true); - ScopePusher for_loop_scope = ScopePusher::for_loop_scope(*this, init); + auto body = parse_statement(); return create_ast_node({ m_source_code, rule_start.position(), position() }, move(init), move(test), move(update), move(body)); @@ -3753,7 +3875,7 @@ NonnullRefPtr Parser::parse_for_in_of_statement(NonnullRefPtr({ m_source_code, rule_start.position(), position() }, move(for_declaration), move(rhs), move(body)); @@ -4862,4 +4984,12 @@ Parser::ForbiddenTokens Parser::ForbiddenTokens::forbid(std::initializer_list Parser::parse_function_node(u16, Optional const&); template NonnullRefPtr Parser::parse_function_node(u16, Optional const&); + +NonnullRefPtr Parser::create_identifier_and_register_in_current_scope(SourceRange range, DeprecatedFlyString string) +{ + auto id = create_ast_node(range, string); + m_state.current_scope_pusher->register_identifier(const_cast(*id)); + return id; +} + } diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index e79376dc669..e3de29fbf51 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -342,6 +342,8 @@ private: } }; + NonnullRefPtr create_identifier_and_register_in_current_scope(SourceRange range, DeprecatedFlyString string); + NonnullRefPtr m_source_code; Vector m_rule_starts; ParserState m_state;