summaryrefslogtreecommitdiff
path: root/Libraries/LibJS
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-04-01 18:51:27 +0200
committerAndreas Kling <kling@serenityos.org>2020-04-01 18:57:00 +0200
commitcd9379dca99ec42aa3b6ef139d1231868be2eeb0 (patch)
tree94778cd0b8cb7450a9d8e807f78ef18157b72fec /Libraries/LibJS
parent021d78f8f7249a84b8e3fb197d6a43e4f92059e8 (diff)
downloadserenity-cd9379dca99ec42aa3b6ef139d1231868be2eeb0.zip
LibJS: Reorganize computing of |this| for CallExpressions
This avoids executing the LHS of the object expression twice when doing a call on the result of an object expression.
Diffstat (limited to 'Libraries/LibJS')
-rw-r--r--Libraries/LibJS/AST.cpp34
-rw-r--r--Libraries/LibJS/AST.h6
2 files changed, 29 insertions, 11 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp
index a20b90d028..549a577e39 100644
--- a/Libraries/LibJS/AST.cpp
+++ b/Libraries/LibJS/AST.cpp
@@ -60,9 +60,30 @@ Value ExpressionStatement::execute(Interpreter& interpreter) const
return m_expression->execute(interpreter);
}
+CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interpreter& interpreter) const
+{
+ if (is_new_expression()) {
+ // Computing |this| is irrelevant for "new" expression.
+ return { {}, m_callee->execute(interpreter) };
+ }
+
+ if (m_callee->is_member_expression()) {
+ auto& member_expression = static_cast<const MemberExpression&>(*m_callee);
+ auto object_value = member_expression.object().execute(interpreter);
+ if (interpreter.exception())
+ return {};
+ auto* this_value = object_value.to_object(interpreter.heap());
+ if (interpreter.exception())
+ return {};
+ auto callee = this_value->get(member_expression.computed_property_name(interpreter)).value_or({});
+ return { this_value, callee };
+ }
+ return { &interpreter.global_object(), m_callee->execute(interpreter) };
+}
+
Value CallExpression::execute(Interpreter& interpreter) const
{
- auto callee = m_callee->execute(interpreter);
+ auto [this_value, callee] = compute_this_and_callee(interpreter);
if (interpreter.exception())
return {};
@@ -89,15 +110,7 @@ Value CallExpression::execute(Interpreter& interpreter) const
new_object->set_prototype(prototype.value().as_object());
call_frame.this_value = new_object;
} else {
- if (m_callee->is_member_expression()) {
- auto object_value = static_cast<const MemberExpression&>(*m_callee).object().execute(interpreter);
- if (interpreter.exception())
- return {};
- auto this_value = object_value.to_object(interpreter.heap());
- if (interpreter.exception())
- return {};
- call_frame.this_value = this_value;
- }
+ call_frame.this_value = this_value;
}
auto result = function->call(interpreter);
@@ -942,5 +955,4 @@ void SwitchCase::dump(int indent) const
statement.dump(indent + 1);
}
}
-
}
diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h
index 80138b2af5..f03b04b111 100644
--- a/Libraries/LibJS/AST.h
+++ b/Libraries/LibJS/AST.h
@@ -484,6 +484,12 @@ public:
private:
virtual const char* class_name() const override { return "CallExpression"; }
+ struct ThisAndCallee {
+ Value this_value;
+ Value callee;
+ };
+ ThisAndCallee compute_this_and_callee(Interpreter&) const;
+
NonnullRefPtr<Expression> m_callee;
const NonnullRefPtrVector<Expression> m_arguments;
};