浏览代码

LibJS: Elide empty lexical environment in for..in/of blocks

When all the variables in a for..in/of block's lexical scope have been
turned into locals, we don't need to create and immediately abandon an
empty environment for them.

This avoid environment allocation in cases like this:

    function foo(a) {
        for (const x of a) {
        }
    }
Andreas Kling 1 年之前
父节点
当前提交
c9f0f0fc70
共有 1 个文件被更改,包括 30 次插入18 次删除
  1. 30 18
      Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

+ 30 - 18
Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp

@@ -2872,8 +2872,6 @@ static Bytecode::CodeGenerationErrorOr<void> 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();
-        has_lexical_binding = true;
 
         // 14.7.5.4 Runtime Semantics: ForDeclarationBindingInstantiation, https://tc39.es/ecma262/#sec-runtime-semantics-fordeclarationbindinginstantiation
         // 1. Assert: environment is a declarative Environment Record.
@@ -2881,24 +2879,35 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
         auto& variable_declaration = static_cast<VariableDeclaration const&>(*lhs.get<NonnullRefPtr<ASTNode const>>());
         // 2. For each element name of the BoundNames of ForBinding, do
         // NOTE: Nothing in the callback throws an exception.
+
+        auto has_non_local_variables = false;
         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<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, true, false, true);
-            }
-            // b. Else,
-            else {
-                // i. Perform ! environment.CreateMutableBinding(name, false).
-                generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
-            }
+            if (!identifier.is_local())
+                has_non_local_variables = true;
         }));
-        // 3. Return unused.
-        // NOTE: No need to do that as we've inlined this.
 
+        if (has_non_local_variables) {
+            generator.begin_variable_scope();
+            has_lexical_binding = true;
+
+            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<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, true, false, true);
+                }
+                // b. Else,
+                else {
+                    // i. Perform ! environment.CreateMutableBinding(name, false).
+                    generator.emit<Bytecode::Op::CreateVariable>(interned_identifier, Bytecode::Op::EnvironmentMode::Lexical, false);
+                }
+            }));
+            // 3. Return unused.
+            // NOTE: No need to do that as we've inlined this.
+        }
         // vi. If destructuring is false, then
         if (!destructuring) {
             // 1. Assert: lhs binds a single name.
@@ -2906,7 +2915,10 @@ static Bytecode::CodeGenerationErrorOr<void> for_in_of_body_evaluation(Bytecode:
             auto lhs_name = variable_declaration.declarations().first()->target().get<NonnullRefPtr<Identifier const>>();
             // 3. Let lhsRef be ! ResolveBinding(lhsName).
             // NOTE: We're skipping all the completion stuff that the spec does, as the unwinding mechanism will take case of doing that.
-            generator.emit_set_variable(*lhs_name, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
+            if (lhs_name->is_local())
+                generator.emit<Bytecode::Op::SetLocal>(lhs_name->local_variable_index());
+            else
+                generator.emit_set_variable(*lhs_name, Bytecode::Op::SetVariable::InitializationMode::Initialize, Bytecode::Op::EnvironmentMode::Lexical);
         }
     }
     // i. If destructuring is false, then