diff options
author | Linus Groh <mail@linusgroh.de> | 2021-10-05 12:59:04 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-10-05 14:52:53 +0200 |
commit | 8074bdc04906ec019585f2a5e9208e86b88bf134 (patch) | |
tree | c53f93a9f1b91687b085743aa70422328259119d /Userland/Libraries | |
parent | d609dde7b00d1724035635114a09c6dd8fcb9cbd (diff) | |
download | serenity-8074bdc04906ec019585f2a5e9208e86b88bf134.zip |
LibJS: Skip declarative env in block statement without lexical decls
The idea here is simple: If the block statement doesn't contain any
lexical declarations, we don't need to allocate, initialize and
eventually garbage collect a new declarative environment.
This even makes lookups across nested blocks slightly faster as we don't
have to traverse a chain of empty environments anymore - instead, the
execution context just stores the outermost non-empty one.
This doesn't speed up test-js considerably, but has a noticeable effect
on test262 and real-world web content :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 18 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 3 |
2 files changed, 16 insertions, 5 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 5cd3a2d7c9..8b2bdfe7cb 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -122,13 +122,21 @@ Value BlockStatement::execute(Interpreter& interpreter, GlobalObject& global_obj InterpreterNodeScope node_scope { interpreter, *this }; auto& vm = interpreter.vm(); - Environment* old_environment = vm.running_execution_context().lexical_environment; - ScopeGuard restore_environment = [&] { + + Environment* old_environment { nullptr }; + ArmedScopeGuard restore_environment = [&] { vm.running_execution_context().lexical_environment = old_environment; }; - auto* block_environment = new_declarative_environment(*old_environment); - block_declaration_instantiation(global_object, block_environment); - vm.running_execution_context().lexical_environment = block_environment; + + // Optimization: We only need a new lexical environment if there are any lexical declarations. :^) + if (has_lexical_declarations()) { + old_environment = vm.running_execution_context().lexical_environment; + auto* block_environment = new_declarative_environment(*old_environment); + block_declaration_instantiation(global_object, block_environment); + vm.running_execution_context().lexical_environment = block_environment; + } else { + restore_environment.disarm(); + } auto block_value = evaluate_statements(interpreter, global_object); if (!labels().is_empty() && vm.should_unwind_until(ScopeType::Breakable, labels())) diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index f44c44a991..2baab6fda3 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -183,6 +183,9 @@ public: void add_lexical_declaration(NonnullRefPtr<Declaration> variables); void add_hoisted_function(NonnullRefPtr<FunctionDeclaration> declaration); + [[nodiscard]] bool has_lexical_declarations() const { return !m_lexical_declarations.is_empty(); } + [[nodiscard]] bool has_var_declarations() const { return !m_var_declarations.is_empty(); } + void for_each_lexically_scoped_declaration(IteratorOrVoidFunction<Declaration const&>&& callback) const; void for_each_lexically_declared_name(IteratorOrVoidFunction<FlyString const&>&& callback) const; |