From f235f08e6d58396cb3886bf3449d1ff58e28f4c0 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 8 Mar 2022 12:34:21 -0500 Subject: [PATCH] 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. --- Userland/Libraries/LibJS/AST.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 3a3ead57b6..512ec9e143 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/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 + // 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 { // 1. If perIterationBindings has any elements, then if (let_declarations.is_empty()) return {}; // a. Let lastIterationEnv be the running execution context's LexicalEnvironment. - auto* last_iteration_env = interpreter.lexical_environment(); + auto* last_iteration_env = verify_cast(interpreter.lexical_environment()); // b. Let outer be lastIterationEnv.[[OuterEnv]]. auto* outer = last_iteration_env->outer_environment(); @@ -752,18 +755,21 @@ Completion ForStatement::loop_evaluation(Interpreter& interpreter, GlobalObject& // d. Let thisIterationEnv be NewDeclarativeEnvironment(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 - 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). MUST(this_iteration_env->create_mutable_binding(global_object, name, false)); // 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()); // 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.