From a1692931af6b3c85cdd143efd4dfe0d1a1e7fb98 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 13 Jul 2023 21:30:36 +0200 Subject: [PATCH] LibJS/Bytecode: Skip CreateVariable for locals in "for" loop head CreateVariable is not needed for locals because they are not stored in environment and created binding will not be used. Also if all variables in loop initialization sections are local then CreateLexicalEnvironment and LeaveLexicalEnvironment can also be ommitted. --- .../Libraries/LibJS/Bytecode/ASTCodegen.cpp | 64 ++++++++++++------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 1dbad87d0e..cfe8583ee7 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -829,7 +829,13 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation if (m_init->is_variable_declaration()) { auto& variable_declaration = verify_cast(*m_init); - if (variable_declaration.is_lexical_declaration()) { + auto has_non_local_variables = false; + MUST(variable_declaration.for_each_bound_identifier([&](auto const& identifier) { + if (!identifier.is_local()) + has_non_local_variables = true; + })); + + if (variable_declaration.is_lexical_declaration() && has_non_local_variables) { has_lexical_environment = true; // FIXME: Is Block correct? @@ -837,8 +843,10 @@ Bytecode::CodeGenerationErrorOr ForStatement::generate_labelled_evaluation 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); + MUST(variable_declaration.for_each_bound_identifier([&](auto const& identifier) { + if (identifier.is_local()) + return; + auto index = generator.intern_identifier(identifier.string()); generator.emit(index, Bytecode::Op::EnvironmentMode::Lexical, is_const); })); } @@ -2528,22 +2536,32 @@ static Bytecode::CodeGenerationErrorOr for_in_of_he generator.emit_set_variable(*identifier); } } else { - // 1. Let oldEnv be the running execution context's LexicalEnvironment. - // NOTE: 'uninitializedBoundNames' refers to the lexical bindings (i.e. Const/Let) present in the second and last form. - // 2. If uninitializedBoundNames is not an empty List, then - entered_lexical_scope = true; - // a. Assert: uninitializedBoundNames has no duplicate entries. - // b. Let newEnv be NewDeclarativeEnvironment(oldEnv). - generator.begin_variable_scope(); - // c. For each String name of uninitializedBoundNames, do - // NOTE: Nothing in the callback throws an exception. - MUST(variable_declaration.for_each_bound_name([&](auto const& name) { - // i. Perform ! newEnv.CreateMutableBinding(name, false). - auto identifier = generator.intern_identifier(name); - generator.emit(identifier, Bytecode::Op::EnvironmentMode::Lexical, false); + auto has_non_local_variables = false; + MUST(variable_declaration.for_each_bound_identifier([&](auto const& identifier) { + if (!identifier.is_local()) + has_non_local_variables = true; })); - // d. Set the running execution context's LexicalEnvironment to newEnv. - // NOTE: Done by CreateLexicalEnvironment. + + if (has_non_local_variables) { + // 1. Let oldEnv be the running execution context's LexicalEnvironment. + // NOTE: 'uninitializedBoundNames' refers to the lexical bindings (i.e. Const/Let) present in the second and last form. + // 2. If uninitializedBoundNames is not an empty List, then + entered_lexical_scope = true; + // a. Assert: uninitializedBoundNames has no duplicate entries. + // b. Let newEnv be NewDeclarativeEnvironment(oldEnv). + generator.begin_variable_scope(); + // c. For each String name of uninitializedBoundNames, do + // NOTE: Nothing in the callback throws an exception. + MUST(variable_declaration.for_each_bound_identifier([&](auto const& identifier) { + if (identifier.is_local()) + return; + // i. Perform ! newEnv.CreateMutableBinding(name, false). + auto interned_identifier = generator.intern_identifier(identifier.string()); + generator.emit(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); + })); + // d. Set the running execution context's LexicalEnvironment to newEnv. + // NOTE: Done by CreateLexicalEnvironment. + } } } else { // Runtime Semantics: ForInOfLoopEvaluation, for any of: @@ -2691,17 +2709,19 @@ static Bytecode::CodeGenerationErrorOr for_in_of_body_evaluation(Bytecode: auto& variable_declaration = static_cast(*lhs.get>()); // 2. For each element name of the BoundNames of ForBinding, do // NOTE: Nothing in the callback throws an exception. - MUST(variable_declaration.for_each_bound_name([&](auto const& name) { - auto identifier = generator.intern_identifier(name); + MUST(variable_declaration.for_each_bound_identifier([&](auto const& identifier) { + if (identifier.is_local()) + return; + auto interned_identifier = generator.intern_identifier(identifier.string()); // a. If IsConstantDeclaration of LetOrConst is true, then if (variable_declaration.is_constant_declaration()) { // i. Perform ! environment.CreateImmutableBinding(name, true). - generator.emit(identifier, Bytecode::Op::EnvironmentMode::Lexical, true); + generator.emit(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, true); } // b. Else, else { // i. Perform ! environment.CreateMutableBinding(name, false). - generator.emit(identifier, Bytecode::Op::EnvironmentMode::Lexical, false); + generator.emit(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false); } })); // 3. Return unused.