1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 18:57:42 +00:00

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.
This commit is contained in:
Andreas Kling 2020-04-01 18:51:27 +02:00
parent 021d78f8f7
commit cd9379dca9
2 changed files with 29 additions and 11 deletions

View file

@ -60,9 +60,30 @@ Value ExpressionStatement::execute(Interpreter& interpreter) const
return m_expression->execute(interpreter); 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 Value CallExpression::execute(Interpreter& interpreter) const
{ {
auto callee = m_callee->execute(interpreter); auto [this_value, callee] = compute_this_and_callee(interpreter);
if (interpreter.exception()) if (interpreter.exception())
return {}; return {};
@ -89,15 +110,7 @@ Value CallExpression::execute(Interpreter& interpreter) const
new_object->set_prototype(prototype.value().as_object()); new_object->set_prototype(prototype.value().as_object());
call_frame.this_value = new_object; call_frame.this_value = new_object;
} else { } else {
if (m_callee->is_member_expression()) { call_frame.this_value = this_value;
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;
}
} }
auto result = function->call(interpreter); auto result = function->call(interpreter);
@ -942,5 +955,4 @@ void SwitchCase::dump(int indent) const
statement.dump(indent + 1); statement.dump(indent + 1);
} }
} }
} }

View file

@ -484,6 +484,12 @@ public:
private: private:
virtual const char* class_name() const override { return "CallExpression"; } 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; NonnullRefPtr<Expression> m_callee;
const NonnullRefPtrVector<Expression> m_arguments; const NonnullRefPtrVector<Expression> m_arguments;
}; };