瀏覽代碼

LibJS: Use known binding indices when creating new for-loop environments

When the initialization statement of a for-loop uses 'let', we must
create a new environment for each iteration of the for loop. The
bindings of the initialization statement are copied over to the new
environment. Since the bindings are created in the same order each time,
we can use that order to directly initialize the bindings and avoid any
O(n) lookups in this hot loop.
Timothy Flynn 3 年之前
父節點
當前提交
f235f08e6d
共有 1 個文件被更改,包括 10 次插入4 次删除
  1. 10 4
      Userland/Libraries/LibJS/AST.cpp

+ 10 - 4
Userland/Libraries/LibJS/AST.cpp

@@ -736,13 +736,16 @@ Completion ForStatement::loop_evaluation(Interpreter& interpreter, GlobalObject&
     }
     }
 
 
     // 14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings ), https://tc39.es/ecma262/#sec-createperiterationenvironment
     // 14.7.4.4 CreatePerIterationEnvironment ( perIterationBindings ), https://tc39.es/ecma262/#sec-createperiterationenvironment
+    // NOTE: Our implementation of this AO is heavily dependent on DeclarativeEnvironment using a Vector with constant indices.
+    //       For performance, we can take advantage of the fact that the declarations of the initialization statement are created
+    //       in the same order each time CreatePerIterationEnvironment is invoked.
     auto create_per_iteration_environment = [&]() -> ThrowCompletionOr<void> {
     auto create_per_iteration_environment = [&]() -> ThrowCompletionOr<void> {
         // 1. If perIterationBindings has any elements, then
         // 1. If perIterationBindings has any elements, then
         if (let_declarations.is_empty())
         if (let_declarations.is_empty())
             return {};
             return {};
 
 
         // a. Let lastIterationEnv be the running execution context's LexicalEnvironment.
         // a. Let lastIterationEnv be the running execution context's LexicalEnvironment.
-        auto* last_iteration_env = interpreter.lexical_environment();
+        auto* last_iteration_env = verify_cast<DeclarativeEnvironment>(interpreter.lexical_environment());
 
 
         // b. Let outer be lastIterationEnv.[[OuterEnv]].
         // b. Let outer be lastIterationEnv.[[OuterEnv]].
         auto* outer = last_iteration_env->outer_environment();
         auto* outer = last_iteration_env->outer_environment();
@@ -752,18 +755,21 @@ Completion ForStatement::loop_evaluation(Interpreter& interpreter, GlobalObject&
 
 
         // d. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
         // d. Let thisIterationEnv be NewDeclarativeEnvironment(outer).
         auto* this_iteration_env = new_declarative_environment(*outer);
         auto* this_iteration_env = new_declarative_environment(*outer);
+        this_iteration_env->ensure_capacity(let_declarations.size());
 
 
         // e. For each element bn of perIterationBindings, do
         // e. For each element bn of perIterationBindings, do
-        for (auto& name : let_declarations) {
+        for (size_t declaration_index = 0; declaration_index < let_declarations.size(); ++declaration_index) {
+            auto const& name = let_declarations[declaration_index];
+
             // i. Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
             // i. Perform ! thisIterationEnv.CreateMutableBinding(bn, false).
             MUST(this_iteration_env->create_mutable_binding(global_object, name, false));
             MUST(this_iteration_env->create_mutable_binding(global_object, name, false));
 
 
             // ii. Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
             // ii. Let lastValue be ? lastIterationEnv.GetBindingValue(bn, true).
-            auto last_value = TRY(last_iteration_env->get_binding_value(global_object, name, true));
+            auto last_value = TRY(last_iteration_env->get_binding_value_direct(global_object, declaration_index, true));
             VERIFY(!last_value.is_empty());
             VERIFY(!last_value.is_empty());
 
 
             // iii. Perform thisIterationEnv.InitializeBinding(bn, lastValue).
             // iii. Perform thisIterationEnv.InitializeBinding(bn, lastValue).
-            MUST(this_iteration_env->initialize_binding(global_object, name, last_value));
+            MUST(this_iteration_env->initialize_binding_direct(global_object, declaration_index, last_value));
         }
         }
 
 
         // f. Set the running execution context's LexicalEnvironment to thisIterationEnv.
         // f. Set the running execution context's LexicalEnvironment to thisIterationEnv.