diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 5cbd85f117..45178bc216 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -29,62 +29,30 @@ Bytecode::CodeGenerationErrorOr ASTNode::generate_bytecode(Bytecode::Gener Bytecode::CodeGenerationErrorOr ScopeNode::generate_bytecode(Bytecode::Generator& generator) const { - Optional maybe_error; - size_t pushed_scope_count = 0; - auto const failing_completion = Completion(Completion::Type::Throw, {}, {}); - // Note: SwitchStatement has its own codegen, but still calls into this function to handle the scoping of the switch body. auto is_switch_statement = is(*this); + bool did_create_lexical_environment = false; + if (is(*this) || is_switch_statement) { - // Perform the steps of BlockDeclarationInstantiation. if (has_lexical_declarations()) { - generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical, Bytecode::Generator::SurroundingScopeKind::Block); - pushed_scope_count++; + generator.block_declaration_instantiation(*this); + did_create_lexical_environment = true; } - - (void)for_each_lexically_scoped_declaration([&](Declaration const& declaration) -> ThrowCompletionOr { - auto is_constant_declaration = declaration.is_constant_declaration(); - // NOTE: Nothing in the callback throws an exception. - MUST(declaration.for_each_bound_name([&](auto const& name) { - auto index = generator.intern_identifier(name); - // NOTE: BlockDeclarationInstantiation takes as input the new lexical environment that was created and checks if there is a binding for the current name only in this new scope. - // For example: `{ let a = 1; { let a = 2; } }`. The second `a` will shadow the first `a` instead of re-initializing or setting it. - if (is_constant_declaration || !generator.has_binding_in_current_scope(index)) { - generator.register_binding(index); - generator.emit(index, Bytecode::Op::EnvironmentMode::Lexical, is_constant_declaration); - } - })); - - if (is(declaration)) { - auto& function_declaration = static_cast(declaration); - auto const& name = function_declaration.name(); - auto index = generator.intern_identifier(name); - generator.emit(function_declaration); - generator.emit(index, Bytecode::Op::SetVariable::InitializationMode::InitializeOrSet); - } - - return {}; - }); - if (is_switch_statement) return {}; - } else if (is(*this)) { // GlobalDeclarationInstantiation is handled by the C++ AO. } else { // FunctionDeclarationInstantiation is handled by the C++ AO. } - if (maybe_error.has_value()) - return maybe_error.release_value(); - for (auto& child : children()) { TRY(child->generate_bytecode(generator)); if (generator.is_current_block_terminated()) break; } - for (size_t i = 0; i < pushed_scope_count; ++i) + if (did_create_lexical_environment) generator.end_variable_scope(); return {}; @@ -788,13 +756,12 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation has_lexical_environment = true; // FIXME: Is Block correct? - generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical, Bytecode::Generator::SurroundingScopeKind::Block); + generator.begin_variable_scope(); bool is_const = variable_declaration.is_constant_declaration(); // NOTE: Nothing in the callback throws an exception. MUST(variable_declaration.for_each_bound_name([&](auto const& name) { auto index = generator.intern_identifier(name); - generator.register_binding(index); generator.emit(index, Bytecode::Op::EnvironmentMode::Lexical, is_const); })); } @@ -994,7 +961,7 @@ Bytecode::CodeGenerationErrorOr FunctionExpression::generate_bytecode(Byte Optional name_identifier; if (has_name) { - generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical); + generator.begin_variable_scope(); name_identifier = generator.intern_identifier(name()); generator.emit(*name_identifier, Bytecode::Op::EnvironmentMode::Lexical, true); @@ -2036,12 +2003,11 @@ Bytecode::CodeGenerationErrorOr TryStatement::generate_bytecode(Bytecode:: if (!m_finalizer) generator.emit(); - generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical, Bytecode::Generator::SurroundingScopeKind::Block); + generator.begin_variable_scope(); TRY(m_handler->parameter().visit( [&](DeprecatedFlyString const& parameter) -> Bytecode::CodeGenerationErrorOr { if (!parameter.is_empty()) { auto parameter_identifier = generator.intern_identifier(parameter); - generator.register_binding(parameter_identifier); generator.emit(parameter_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); generator.emit(parameter_identifier, Bytecode::Op::SetVariable::InitializationMode::Initialize); } @@ -2313,7 +2279,6 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he MUST(variable_declaration.for_each_bound_name([&](auto const& name) { // i. Perform ! newEnv.CreateMutableBinding(name, false). auto identifier = generator.intern_identifier(name); - generator.register_binding(identifier); generator.emit(identifier, Bytecode::Op::EnvironmentMode::Lexical, false); })); // d. Set the running execution context's LexicalEnvironment to newEnv. @@ -2461,7 +2426,7 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: // iii. Let iterationEnv be NewDeclarativeEnvironment(oldEnv). // iv. Perform ForDeclarationBindingInstantiation of lhs with argument iterationEnv. // v. Set the running execution context's LexicalEnvironment to iterationEnv. - generator.begin_variable_scope(Bytecode::Generator::BindingMode::Lexical); + generator.begin_variable_scope(); has_lexical_binding = true; // 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation @@ -2472,7 +2437,6 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: // NOTE: Nothing in the callback throws an exception. MUST(variable_declaration.for_each_bound_name([&](auto const& name) { auto identifier = generator.intern_identifier(name); - generator.register_binding(identifier, Bytecode::Generator::BindingMode::Lexical); // a. If IsConstantDeclaration of LetOrConst is true, then if (variable_declaration.is_constant_declaration()) { // i. Perform ! environment.CreateImmutableBinding(name, true). diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index 320ba9bf52..e3c436a8d5 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -87,30 +87,24 @@ Label Generator::nearest_continuable_scope() const return m_continuable_scopes.last().bytecode_target; } -void Generator::begin_variable_scope(BindingMode mode, SurroundingScopeKind kind) +void Generator::block_declaration_instantiation(ScopeNode const& scope_node) { - m_variable_scopes.append({ kind, mode, {} }); - if (mode != BindingMode::Global) { - start_boundary(mode == BindingMode::Lexical ? BlockBoundaryType::LeaveLexicalEnvironment : BlockBoundaryType::LeaveVariableEnvironment); - emit( - mode == BindingMode::Lexical - ? Bytecode::Op::EnvironmentMode::Lexical - : Bytecode::Op::EnvironmentMode::Var); - } + start_boundary(BlockBoundaryType::LeaveLexicalEnvironment); + emit(scope_node); +} + +void Generator::begin_variable_scope() +{ + start_boundary(BlockBoundaryType::LeaveLexicalEnvironment); + emit(Bytecode::Op::EnvironmentMode::Lexical); } void Generator::end_variable_scope() { - auto mode = m_variable_scopes.take_last().mode; - if (mode != BindingMode::Global) { - end_boundary(mode == BindingMode::Lexical ? BlockBoundaryType::LeaveLexicalEnvironment : BlockBoundaryType::LeaveVariableEnvironment); + end_boundary(BlockBoundaryType::LeaveLexicalEnvironment); - if (!m_current_basic_block->is_terminated()) { - emit( - mode == BindingMode::Lexical - ? Bytecode::Op::EnvironmentMode::Lexical - : Bytecode::Op::EnvironmentMode::Var); - } + if (!m_current_basic_block->is_terminated()) { + emit(Bytecode::Op::EnvironmentMode::Lexical); } } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index f4d9dbdf84..db6589679a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -136,36 +136,11 @@ public: }; struct LexicalScope { SurroundingScopeKind kind; - BindingMode mode; - HashTable known_bindings; }; - void register_binding(IdentifierTableIndex identifier, BindingMode mode = BindingMode::Lexical) - { - m_variable_scopes.last_matching([&](auto& x) { return x.mode == BindingMode::Global || x.mode == mode; })->known_bindings.set(identifier); - } - bool has_binding(IdentifierTableIndex identifier, Optional const& specific_binding_mode = {}) const - { - for (auto index = m_variable_scopes.size(); index > 0; --index) { - auto& scope = m_variable_scopes[index - 1]; + void block_declaration_instantiation(ScopeNode const&); - if (scope.mode != BindingMode::Global && specific_binding_mode.value_or(scope.mode) != scope.mode) - continue; - - if (scope.known_bindings.contains(identifier)) - return true; - } - return false; - } - bool has_binding_in_current_scope(IdentifierTableIndex identifier) const - { - if (m_variable_scopes.is_empty()) - return false; - - return m_variable_scopes.last().known_bindings.contains(identifier); - } - - void begin_variable_scope(BindingMode mode = BindingMode::Lexical, SurroundingScopeKind kind = SurroundingScopeKind::Block); + void begin_variable_scope(); void end_variable_scope(); enum class BlockBoundaryType { @@ -239,7 +214,6 @@ private: FunctionKind m_enclosing_function_kind { FunctionKind::Normal }; Vector m_continuable_scopes; Vector m_breakable_scopes; - Vector m_variable_scopes; Vector m_boundaries; Vector m_home_objects; }; diff --git a/Userland/Libraries/LibJS/Bytecode/Instruction.h b/Userland/Libraries/LibJS/Bytecode/Instruction.h index a581d9af22..3b36c4c364 100644 --- a/Userland/Libraries/LibJS/Bytecode/Instruction.h +++ b/Userland/Libraries/LibJS/Bytecode/Instruction.h @@ -17,6 +17,7 @@ O(BitwiseNot) \ O(BitwiseOr) \ O(BitwiseXor) \ + O(BlockDeclarationInstantiation) \ O(Call) \ O(ConcatString) \ O(ContinuePendingUnwind) \ diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 7ffde48f20..4534eb18a0 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -1097,6 +1097,16 @@ ThrowCompletionOr ToNumeric::execute_impl(Bytecode::Interpreter& interpret return {}; } +ThrowCompletionOr BlockDeclarationInstantiation::execute_impl(Bytecode::Interpreter& interpreter) const +{ + auto& vm = interpreter.vm(); + auto old_environment = vm.running_execution_context().lexical_environment; + interpreter.saved_lexical_environment_stack().append(old_environment); + vm.running_execution_context().lexical_environment = new_declarative_environment(*old_environment); + m_scope_node.block_declaration_instantiation(vm, vm.running_execution_context().lexical_environment); + return {}; +} + DeprecatedString Load::to_deprecated_string_impl(Bytecode::Executable const&) const { return DeprecatedString::formatted("Load {}", m_src); @@ -1459,4 +1469,9 @@ DeprecatedString ToNumeric::to_deprecated_string_impl(Bytecode::Executable const return "ToNumeric"sv; } +DeprecatedString BlockDeclarationInstantiation::to_deprecated_string_impl(Bytecode::Executable const&) const +{ + return "BlockDeclarationInstantiation"sv; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 11449abec8..5ee823e958 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -794,6 +794,23 @@ private: Optional m_home_object; }; +class BlockDeclarationInstantiation final : public Instruction { +public: + explicit BlockDeclarationInstantiation(ScopeNode const& scope_node) + : Instruction(Type::BlockDeclarationInstantiation) + , m_scope_node(scope_node) + { + } + + ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; + DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; + void replace_references_impl(BasicBlock const&, BasicBlock const&) { } + void replace_references_impl(Register, Register) { } + +private: + ScopeNode const& m_scope_node; +}; + class Return final : public Instruction { public: constexpr static bool IsTerminator = true;