Browse Source

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.
Andreas Kling 5 years ago
parent
commit
cd9379dca9
2 changed files with 29 additions and 11 deletions
  1. 23 11
      Libraries/LibJS/AST.cpp
  2. 6 0
      Libraries/LibJS/AST.h

+ 23 - 11
Libraries/LibJS/AST.cpp

@@ -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()) {
-            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);
     auto result = function->call(interpreter);
@@ -942,5 +955,4 @@ void SwitchCase::dump(int indent) const
         statement.dump(indent + 1);
         statement.dump(indent + 1);
     }
     }
 }
 }
-
 }
 }

+ 6 - 0
Libraries/LibJS/AST.h

@@ -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;
 };
 };