summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorLinus Groh <mail@linusgroh.de>2021-10-05 12:59:04 +0100
committerAndreas Kling <kling@serenityos.org>2021-10-05 14:52:53 +0200
commit8074bdc04906ec019585f2a5e9208e86b88bf134 (patch)
treec53f93a9f1b91687b085743aa70422328259119d /Userland/Libraries
parentd609dde7b00d1724035635114a09c6dd8fcb9cbd (diff)
downloadserenity-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.cpp18
-rw-r--r--Userland/Libraries/LibJS/AST.h3
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;