diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 87c763a49cf..85435f7861d 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -826,7 +826,7 @@ void BindingPattern::dump(int indent) const } } -FunctionNode::FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names) +FunctionNode::FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names) : m_name(move(name)) , m_source_text(move(source_text)) , m_body(move(body)) diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index c58527efec8..d609b343049 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -340,11 +341,11 @@ public: 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(FlyString name) + auto const& local_variables_names() const { return m_local_variables_names; } + size_t add_local_variable(FlyString name, LocalVariable::DeclarationKind declaration_kind) { auto index = m_local_variables_names.size(); - m_local_variables_names.append(move(name)); + m_local_variables_names.append({ move(name), declaration_kind }); return index; } @@ -363,7 +364,7 @@ private: Vector> m_functions_hoistable_with_annexB_extension; - Vector m_local_variables_names; + Vector m_local_variables_names; }; // ImportEntry Record, https://tc39.es/ecma262/#table-importentry-record-fields @@ -785,7 +786,7 @@ public: auto const& body_ptr() const { return m_body; } auto 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; } + 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_parsing_insights.might_need_arguments_object; } bool contains_direct_call_to_eval() const { return m_parsing_insights.contains_direct_call_to_eval; } @@ -803,7 +804,7 @@ public: virtual ~FunctionNode(); protected: - FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names); + FunctionNode(RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights parsing_insights, bool is_arrow_function, Vector local_variables_names); void dump(int indent, ByteString const& class_name) const; RefPtr m_name { nullptr }; @@ -818,7 +819,7 @@ private: bool m_is_arrow_function : 1 { false }; FunctionParsingInsights m_parsing_insights; - Vector m_local_variables_names; + Vector m_local_variables_names; mutable RefPtr m_shared_data; }; @@ -829,7 +830,7 @@ class FunctionDeclaration final public: static bool must_have_name() { return true; } - FunctionDeclaration(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights insights, Vector local_variables_names) + FunctionDeclaration(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights insights, Vector local_variables_names) : Declaration(move(source_range)) , FunctionNode(move(name), move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, insights, false, move(local_variables_names)) { @@ -859,7 +860,7 @@ class FunctionExpression final public: static bool must_have_name() { return false; } - FunctionExpression(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights insights, Vector local_variables_names, bool is_arrow_function = false) + FunctionExpression(SourceRange source_range, RefPtr name, ByteString source_text, NonnullRefPtr body, NonnullRefPtr parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, FunctionParsingInsights insights, Vector local_variables_names, bool is_arrow_function = false) : Expression(move(source_range)) , FunctionNode(move(name), move(source_text), move(body), move(parameters), function_length, kind, is_strict_mode, insights, is_arrow_function, move(local_variables_names)) { diff --git a/Libraries/LibJS/Bytecode/Executable.h b/Libraries/LibJS/Bytecode/Executable.h index 6354b374d3a..08f9b2c3c95 100644 --- a/Libraries/LibJS/Bytecode/Executable.h +++ b/Libraries/LibJS/Bytecode/Executable.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -85,7 +86,7 @@ public: HashMap source_map; - Vector local_variable_names; + Vector local_variable_names; size_t local_index_base { 0 }; size_t argument_index_base { 0 }; diff --git a/Libraries/LibJS/Bytecode/Generator.cpp b/Libraries/LibJS/Bytecode/Generator.cpp index 32e33257315..88c1d326a30 100644 --- a/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Libraries/LibJS/Bytecode/Generator.cpp @@ -56,7 +56,7 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E if (function.shared_data().m_arguments_object_needed) { Optional dst; - auto local_var_index = function.shared_data().m_local_variables_names.find_first_index("arguments"_fly_string); + auto local_var_index = function.shared_data().m_local_variables_names.find_first_index_if([](auto const& local) { return local.declaration_kind == LocalVariable::DeclarationKind::ArgumentsObject; }); if (local_var_index.has_value()) dst = local(Identifier::Local::variable(local_var_index.value())); @@ -215,9 +215,10 @@ CodeGenerationErrorOr Generator::emit_function_declaration_instantiation(E return {}; } -CodeGenerationErrorOr> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr function, MustPropagateCompletion must_propagate_completion, Vector local_variable_names) +CodeGenerationErrorOr> Generator::compile(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind, GC::Ptr function, MustPropagateCompletion must_propagate_completion, Vector local_variable_names) { Generator generator(vm, function, must_propagate_completion); + generator.m_local_variables = local_variable_names; generator.switch_to_basic_block(generator.make_block()); SourceLocationScope scope(generator, node); @@ -483,7 +484,7 @@ CodeGenerationErrorOr> Generator::compile(VM& vm, ASTNode co CodeGenerationErrorOr> Generator::generate_from_ast_node(VM& vm, ASTNode const& node, FunctionKind enclosing_function_kind) { - Vector local_variable_names; + Vector local_variable_names; if (is(node)) local_variable_names = static_cast(node).local_variables_names(); return compile(vm, node, enclosing_function_kind, {}, MustPropagateCompletion::Yes, move(local_variable_names)); @@ -1211,6 +1212,13 @@ void Generator::set_local_initialized(Identifier::Local const& local) } } +bool Generator::is_local_lexically_declared(Identifier::Local const& local) const +{ + if (local.is_argument()) + return false; + return m_local_variables[local.index].declaration_kind == LocalVariable::DeclarationKind::LetOrConst; +} + ScopedOperand Generator::get_this(Optional preferred_dst) { if (m_current_basic_block->has_resolved_this()) diff --git a/Libraries/LibJS/Bytecode/Generator.h b/Libraries/LibJS/Bytecode/Generator.h index 3adfa179d80..e906b1e5e6b 100644 --- a/Libraries/LibJS/Bytecode/Generator.h +++ b/Libraries/LibJS/Bytecode/Generator.h @@ -53,6 +53,7 @@ public: void set_local_initialized(Identifier::Local const&); [[nodiscard]] bool is_local_initialized(u32 local_index) const; [[nodiscard]] bool is_local_initialized(Identifier::Local const&) const; + [[nodiscard]] bool is_local_lexically_declared(Identifier::Local const& local) const; class SourceLocationScope { public: @@ -358,7 +359,7 @@ public: private: VM& m_vm; - static CodeGenerationErrorOr> compile(VM&, ASTNode const&, FunctionKind, GC::Ptr, MustPropagateCompletion, Vector local_variable_names); + static CodeGenerationErrorOr> compile(VM&, ASTNode const&, FunctionKind, GC::Ptr, MustPropagateCompletion, Vector local_variable_names); enum class JumpType { Continue, @@ -413,6 +414,7 @@ private: HashTable m_initialized_locals; HashTable m_initialized_arguments; + Vector m_local_variables; bool m_finished { false }; bool m_must_propagate_completion { true }; diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 7cd078d4402..4070fc59ea3 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -83,7 +83,7 @@ static ByteString format_operand(StringView name, Operand operand, Bytecode::Exe } break; case Operand::Type::Local: - builder.appendff("\033[34m{}~{}\033[0m", executable.local_variable_names[operand.index() - executable.local_index_base], operand.index() - executable.local_index_base); + builder.appendff("\033[34m{}~{}\033[0m", executable.local_variable_names[operand.index() - executable.local_index_base].name, operand.index() - executable.local_index_base); break; case Operand::Type::Argument: builder.appendff("\033[34marg{}\033[0m", operand.index() - executable.argument_index_base); diff --git a/Libraries/LibJS/LocalVariable.h b/Libraries/LibJS/LocalVariable.h new file mode 100644 index 00000000000..7d947ab44bd --- /dev/null +++ b/Libraries/LibJS/LocalVariable.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +struct LocalVariable { + FlyString name; + enum class DeclarationKind { + Var, + LetOrConst, + Function, + ArgumentsObject, + CatchClauseParameter + }; + DeclarationKind declaration_kind; +}; + +} diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 7019a0d3577..13e69178eac 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -298,17 +298,22 @@ public: } } - bool scope_has_declaration = false; - if (is_top_level() && m_var_names.contains(identifier_group_name)) - scope_has_declaration = true; - else if (m_lexical_names.contains(identifier_group_name) || m_function_names.contains(identifier_group_name)) - scope_has_declaration = true; + Optional local_variable_declaration_kind; + if (is_top_level() && m_var_names.contains(identifier_group_name)) { + local_variable_declaration_kind = LocalVariable::DeclarationKind::Var; + } else if (m_lexical_names.contains(identifier_group_name)) { + local_variable_declaration_kind = LocalVariable::DeclarationKind::LetOrConst; + } else if (m_function_names.contains(identifier_group_name)) { + local_variable_declaration_kind = LocalVariable::DeclarationKind::Function; + } - if (m_type == ScopeType::Function && !m_is_arrow_function && identifier_group_name == "arguments"sv) - scope_has_declaration = true; + if (m_type == ScopeType::Function && !m_is_arrow_function && identifier_group_name == "arguments"sv) { + local_variable_declaration_kind = LocalVariable::DeclarationKind::ArgumentsObject; + } - if (m_type == ScopeType::Catch && m_catch_parameter_names.contains(identifier_group_name)) - scope_has_declaration = true; + if (m_type == ScopeType::Catch && m_catch_parameter_names.contains(identifier_group_name)) { + local_variable_declaration_kind = LocalVariable::DeclarationKind::CatchClauseParameter; + } bool hoistable_function_declaration = false; for (auto const& function_declaration : m_functions_to_hoist) { @@ -329,7 +334,7 @@ public: if (m_type == ScopeType::ClassDeclaration) { // NOTE: Class declaration doesn't not have own ScopeNode hence can't contain declaration of any variable - scope_has_declaration = false; + local_variable_declaration_kind.clear(); } bool is_function_parameter = false; @@ -353,7 +358,7 @@ public: for (auto& identifier : identifier_group.identifiers) identifier->set_is_global(); } - } else if (scope_has_declaration || is_function_parameter) { + } else if (local_variable_declaration_kind.has_value() || is_function_parameter) { if (hoistable_function_declaration) continue; @@ -380,7 +385,7 @@ public: for (auto& identifier : identifier_group.identifiers) identifier->set_argument_index(argument_index.value()); } else { - auto local_variable_index = local_scope->m_node->add_local_variable(identifier_group_name); + auto local_variable_index = local_scope->m_node->add_local_variable(identifier_group_name, *local_variable_declaration_kind); for (auto& identifier : identifier_group.identifiers) identifier->set_local_variable_index(local_variable_index); } @@ -1656,7 +1661,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_ constructor = create_ast_node( { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), FunctionParameters::create(Vector { FunctionParameter { move(argument_name), nullptr, true } }), 0, FunctionKind::Normal, - /* is_strict_mode */ true, parsing_insights, /* local_variables_names */ Vector {}); + /* is_strict_mode */ true, parsing_insights, /* local_variables_names */ Vector {}); } else { FunctionParsingInsights parsing_insights; parsing_insights.uses_this_from_environment = true; @@ -1664,7 +1669,7 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_ constructor = create_ast_node( { m_source_code, rule_start.position(), position() }, class_name, "", move(constructor_body), FunctionParameters::empty(), 0, FunctionKind::Normal, - /* is_strict_mode */ true, parsing_insights, /* local_variables_names */ Vector {}); + /* is_strict_mode */ true, parsing_insights, /* local_variables_names */ Vector {}); } } diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index f4887e07241..7be4db2550d 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -33,7 +33,7 @@ namespace JS { GC_DEFINE_ALLOCATOR(ECMAScriptFunctionObject); -GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) +GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) { Object* prototype = nullptr; switch (kind) { @@ -73,7 +73,7 @@ GC::Ref ECMAScriptFunctionObject::create(Realm& realm, *prototype); } -GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) +GC::Ref ECMAScriptFunctionObject::create(Realm& realm, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind kind, bool is_strict, FunctionParsingInsights parsing_insights, bool is_arrow_function, Variant class_field_initializer_name) { auto shared_data = adopt_ref(*new SharedFunctionInstanceData( realm.vm(), @@ -153,7 +153,7 @@ SharedFunctionInstanceData::SharedFunctionInstanceData( bool strict, bool is_arrow_function, FunctionParsingInsights const& parsing_insights, - Vector local_variables_names) + Vector local_variables_names) : m_formal_parameters(move(formal_parameters)) , m_ecmascript_code(move(ecmascript_code)) , m_name(move(name)) @@ -295,7 +295,7 @@ SharedFunctionInstanceData::SharedFunctionInstanceData( HashMap parameter_bindings; - auto arguments_object_needs_binding = m_arguments_object_needed && !m_local_variables_names.contains_slow(vm.names.arguments.as_string()); + auto arguments_object_needs_binding = m_arguments_object_needed && !m_local_variables_names.first_matching([](auto const& local) { return local.declaration_kind == LocalVariable::DeclarationKind::ArgumentsObject; }).has_value(); // 22. If argumentsObjectNeeded is true, then if (m_arguments_object_needed) { diff --git a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 8758b2e81fa..ec1ce60ea8a 100644 --- a/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -46,7 +46,7 @@ public: bool strict, bool is_arrow_function, FunctionParsingInsights const&, - Vector local_variables_names); + Vector local_variables_names); RefPtr m_formal_parameters; // [[FormalParameters]] RefPtr m_ecmascript_code; // [[ECMAScriptCode]] @@ -54,7 +54,7 @@ public: FlyString m_name; ByteString m_source_text; // [[SourceText]] - Vector m_local_variables_names; + Vector m_local_variables_names; i32 m_function_length { 0 }; @@ -103,8 +103,8 @@ class ECMAScriptFunctionObject final : public FunctionObject { GC_DECLARE_ALLOCATOR(ECMAScriptFunctionObject); public: - static GC::Ref create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); - static GC::Ref create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + static GC::Ref create(Realm&, FlyString name, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); + static GC::Ref create(Realm&, FlyString name, Object& prototype, ByteString source_text, Statement const& ecmascript_code, NonnullRefPtr parameters, i32 function_length, Vector local_variables_names, Environment* parent_environment, PrivateEnvironment* private_environment, FunctionKind, bool is_strict, FunctionParsingInsights, bool is_arrow_function = false, Variant class_field_initializer_name = {}); [[nodiscard]] static GC::Ref create_from_function_node( FunctionNode const&, @@ -167,7 +167,7 @@ public: // Equivalent to absence of [[Construct]] virtual bool has_constructor() const override { return kind() == FunctionKind::Normal && !shared_data().m_is_arrow_function; } - virtual Vector const& local_variables_names() const override { return shared_data().m_local_variables_names; } + virtual Vector const& local_variables_names() const override { return shared_data().m_local_variables_names; } FunctionKind kind() const { return shared_data().m_kind; } diff --git a/Libraries/LibJS/Runtime/FunctionObject.h b/Libraries/LibJS/Runtime/FunctionObject.h index c87945bab3c..e7ba55d5385 100644 --- a/Libraries/LibJS/Runtime/FunctionObject.h +++ b/Libraries/LibJS/Runtime/FunctionObject.h @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,7 @@ public: // [[Realm]] virtual Realm* realm() const { return nullptr; } - virtual Vector const& local_variables_names() const { VERIFY_NOT_REACHED(); } + virtual Vector const& local_variables_names() const { VERIFY_NOT_REACHED(); } virtual FunctionParameters const& formal_parameters() const { VERIFY_NOT_REACHED(); }