Browse Source

LibWeb: Implement calc() value equality check in a more efficient way

Instead of serializing two calc() values to String and then comparing
those strings, we can now compare calc() values by actually traversing
their internal CalculationNode tree.

This makes style recomputation faster on pages with lots of calc()
values since it's now much cheaper to check whether a property with
some calc() value actually changed.
Andreas Kling 1 năm trước cách đây
mục cha
commit
5b7a8891a6

+ 1 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -6552,6 +6552,7 @@ public:
     {
         builder.appendff("{: >{}}UNPARSED({})\n", "", indent, m_component_value.to_debug_string());
     }
+    virtual bool equals(CalculationNode const&) const override { return false; }
 
 private:
     UnparsedCalculationNode(ComponentValue component_value)

+ 265 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.cpp

@@ -237,6 +237,15 @@ void NumericCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}NUMERIC({})\n", "", indent, m_value.visit([](auto& it) { return it.to_string(); }));
 }
 
+bool NumericCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value == static_cast<NumericCalculationNode const&>(other).m_value;
+}
+
 NonnullOwnPtr<SumCalculationNode> SumCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
 {
     return adopt_own(*new (nothrow) SumCalculationNode(move(values)));
@@ -355,6 +364,19 @@ void SumCalculationNode::dump(StringBuilder& builder, int indent) const
         item->dump(builder, indent + 2);
 }
 
+bool SumCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    for (size_t i = 0; i < m_values.size(); ++i) {
+        if (!m_values[i]->equals(*static_cast<SumCalculationNode const&>(other).m_values[i]))
+            return false;
+    }
+    return true;
+}
+
 NonnullOwnPtr<ProductCalculationNode> ProductCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
 {
     return adopt_own(*new (nothrow) ProductCalculationNode(move(values)));
@@ -478,6 +500,19 @@ void ProductCalculationNode::dump(StringBuilder& builder, int indent) const
         item->dump(builder, indent + 2);
 }
 
+bool ProductCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    for (size_t i = 0; i < m_values.size(); ++i) {
+        if (!m_values[i]->equals(*static_cast<ProductCalculationNode const&>(other).m_values[i]))
+            return false;
+    }
+    return true;
+}
+
 NonnullOwnPtr<NegateCalculationNode> NegateCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
 {
     return adopt_own(*new (nothrow) NegateCalculationNode(move(value)));
@@ -532,6 +567,15 @@ void NegateCalculationNode::dump(StringBuilder& builder, int indent) const
     m_value->dump(builder, indent + 2);
 }
 
+bool NegateCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<NegateCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<InvertCalculationNode> InvertCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
 {
     return adopt_own(*new (nothrow) InvertCalculationNode(move(value)));
@@ -593,6 +637,15 @@ void InvertCalculationNode::dump(StringBuilder& builder, int indent) const
     m_value->dump(builder, indent + 2);
 }
 
+bool InvertCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<InvertCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<MinCalculationNode> MinCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
 {
     return adopt_own(*new (nothrow) MinCalculationNode(move(values)));
@@ -675,6 +728,19 @@ void MinCalculationNode::dump(StringBuilder& builder, int indent) const
         value->dump(builder, indent + 2);
 }
 
+bool MinCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    for (size_t i = 0; i < m_values.size(); ++i) {
+        if (!m_values[i]->equals(*static_cast<MinCalculationNode const&>(other).m_values[i]))
+            return false;
+    }
+    return true;
+}
+
 NonnullOwnPtr<MaxCalculationNode> MaxCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
 {
     return adopt_own(*new (nothrow) MaxCalculationNode(move(values)));
@@ -757,6 +823,19 @@ void MaxCalculationNode::dump(StringBuilder& builder, int indent) const
         value->dump(builder, indent + 2);
 }
 
+bool MaxCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    for (size_t i = 0; i < m_values.size(); ++i) {
+        if (!m_values[i]->equals(*static_cast<MaxCalculationNode const&>(other).m_values[i]))
+            return false;
+    }
+    return true;
+}
+
 NonnullOwnPtr<ClampCalculationNode> ClampCalculationNode::create(NonnullOwnPtr<CalculationNode> min, NonnullOwnPtr<CalculationNode> center, NonnullOwnPtr<CalculationNode> max)
 {
     return adopt_own(*new (nothrow) ClampCalculationNode(move(min), move(center), move(max)));
@@ -853,6 +932,17 @@ void ClampCalculationNode::dump(StringBuilder& builder, int indent) const
     m_max_value->dump(builder, indent + 2);
 }
 
+bool ClampCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_min_value->equals(*static_cast<ClampCalculationNode const&>(other).m_min_value)
+        && m_center_value->equals(*static_cast<ClampCalculationNode const&>(other).m_center_value)
+        && m_max_value->equals(*static_cast<ClampCalculationNode const&>(other).m_max_value);
+}
+
 NonnullOwnPtr<AbsCalculationNode> AbsCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) AbsCalculationNode(move(value)));
@@ -915,6 +1005,15 @@ void AbsCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ABS: {}\n", "", indent, to_string());
 }
 
+bool AbsCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<AbsCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<SignCalculationNode> SignCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) SignCalculationNode(move(value)));
@@ -979,6 +1078,15 @@ void SignCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}SIGN: {}\n", "", indent, to_string());
 }
 
+bool SignCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<SignCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<ConstantCalculationNode> ConstantCalculationNode::create(ConstantType constant)
 {
     return adopt_own(*new (nothrow) ConstantCalculationNode(constant));
@@ -1047,6 +1155,15 @@ void ConstantCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}CONSTANT: {}\n", "", indent, to_string());
 }
 
+bool ConstantCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_constant == static_cast<ConstantCalculationNode const&>(other).m_constant;
+}
+
 NonnullOwnPtr<SinCalculationNode> SinCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) SinCalculationNode(move(value)));
@@ -1106,6 +1223,15 @@ void SinCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}SIN: {}\n", "", indent, to_string());
 }
 
+bool SinCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<SinCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<CosCalculationNode> CosCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) CosCalculationNode(move(value)));
@@ -1165,6 +1291,15 @@ void CosCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}COS: {}\n", "", indent, to_string());
 }
 
+bool CosCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<CosCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<TanCalculationNode> TanCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) TanCalculationNode(move(value)));
@@ -1224,6 +1359,15 @@ void TanCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}TAN: {}\n", "", indent, to_string());
 }
 
+bool TanCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<TanCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<AsinCalculationNode> AsinCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) AsinCalculationNode(move(value)));
@@ -1283,6 +1427,15 @@ void AsinCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ASIN: {}\n", "", indent, to_string());
 }
 
+bool AsinCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<AsinCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<AcosCalculationNode> AcosCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) AcosCalculationNode(move(value)));
@@ -1342,6 +1495,15 @@ void AcosCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ACOS: {}\n", "", indent, to_string());
 }
 
+bool AcosCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<AcosCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<AtanCalculationNode> AtanCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) AtanCalculationNode(move(value)));
@@ -1401,6 +1563,15 @@ void AtanCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ATAN: {}\n", "", indent, to_string());
 }
 
+bool AtanCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<AtanCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<Atan2CalculationNode> Atan2CalculationNode::create(NonnullOwnPtr<CalculationNode> y, NonnullOwnPtr<CalculationNode> x)
 {
     return adopt_own(*new (nothrow) Atan2CalculationNode(move(y), move(x)));
@@ -1469,6 +1640,16 @@ void Atan2CalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ATAN2: {}\n", "", indent, to_string());
 }
 
+bool Atan2CalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_x->equals(*static_cast<Atan2CalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<Atan2CalculationNode const&>(other).m_y);
+}
+
 NonnullOwnPtr<PowCalculationNode> PowCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
 {
     return adopt_own(*new (nothrow) PowCalculationNode(move(x), move(y)));
@@ -1532,6 +1713,16 @@ void PowCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}POW: {}\n", "", indent, to_string());
 }
 
+bool PowCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_x->equals(*static_cast<PowCalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<PowCalculationNode const&>(other).m_y);
+}
+
 NonnullOwnPtr<SqrtCalculationNode> SqrtCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) SqrtCalculationNode(move(value)));
@@ -1586,6 +1777,15 @@ void SqrtCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}SQRT: {}\n", "", indent, to_string());
 }
 
+bool SqrtCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<SqrtCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<HypotCalculationNode> HypotCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
 {
     return adopt_own(*new (nothrow) HypotCalculationNode(move(values)));
@@ -1666,6 +1866,19 @@ void HypotCalculationNode::dump(StringBuilder& builder, int indent) const
         value->dump(builder, indent + 2);
 }
 
+bool HypotCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    for (size_t i = 0; i < m_values.size(); ++i) {
+        if (!m_values[i]->equals(*static_cast<HypotCalculationNode const&>(other).m_values[i]))
+            return false;
+    }
+    return true;
+}
+
 NonnullOwnPtr<LogCalculationNode> LogCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
 {
     return adopt_own(*new (nothrow) LogCalculationNode(move(x), move(y)));
@@ -1729,6 +1942,16 @@ void LogCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}LOG: {}\n", "", indent, to_string());
 }
 
+bool LogCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_x->equals(*static_cast<LogCalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<LogCalculationNode const&>(other).m_y);
+}
+
 NonnullOwnPtr<ExpCalculationNode> ExpCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
 {
     return adopt_own(*new (nothrow) ExpCalculationNode(move(value)));
@@ -1783,6 +2006,15 @@ void ExpCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}EXP: {}\n", "", indent, to_string());
 }
 
+bool ExpCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_value->equals(*static_cast<ExpCalculationNode const&>(other).m_value);
+}
+
 NonnullOwnPtr<RoundCalculationNode> RoundCalculationNode::create(RoundingStrategy strategy, NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
 {
     return adopt_own(*new (nothrow) RoundCalculationNode(strategy, move(x), move(y)));
@@ -1885,6 +2117,17 @@ void RoundCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}ROUND: {}\n", "", indent, to_string());
 }
 
+bool RoundCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_strategy == static_cast<RoundCalculationNode const&>(other).m_strategy
+        && m_x->equals(*static_cast<RoundCalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<RoundCalculationNode const&>(other).m_y);
+}
+
 NonnullOwnPtr<ModCalculationNode> ModCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
 {
     return adopt_own(*new (nothrow) ModCalculationNode(move(x), move(y)));
@@ -1961,6 +2204,16 @@ void ModCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}MOD: {}\n", "", indent, to_string());
 }
 
+bool ModCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_x->equals(*static_cast<ModCalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<ModCalculationNode const&>(other).m_y);
+}
+
 NonnullOwnPtr<RemCalculationNode> RemCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
 {
     return adopt_own(*new (nothrow) RemCalculationNode(move(x), move(y)));
@@ -2036,6 +2289,16 @@ void RemCalculationNode::dump(StringBuilder& builder, int indent) const
     builder.appendff("{: >{}}REM: {}\n", "", indent, to_string());
 }
 
+bool RemCalculationNode::equals(CalculationNode const& other) const
+{
+    if (this == &other)
+        return true;
+    if (type() != other.type())
+        return false;
+    return m_x->equals(*static_cast<RemCalculationNode const&>(other).m_x)
+        && m_y->equals(*static_cast<RemCalculationNode const&>(other).m_y);
+}
+
 void CalculatedStyleValue::CalculationResult::add(CalculationResult const& other, Optional<Length::ResolutionContext const&> context, PercentageBasis const& percentage_basis)
 {
     add_or_subtract_internal(SumOperation::Add, other, context, percentage_basis);
@@ -2331,8 +2594,8 @@ bool CalculatedStyleValue::equals(StyleValue const& other) const
 {
     if (type() != other.type())
         return false;
-    // This is a case where comparing the strings actually makes sense.
-    return to_string() == other.to_string();
+
+    return m_calculation->equals(*static_cast<CalculatedStyleValue const&>(other).m_calculation);
 }
 
 Optional<Angle> CalculatedStyleValue::resolve_angle() const

+ 29 - 0
Userland/Libraries/LibWeb/CSS/StyleValues/CalculatedStyleValue.h

@@ -65,6 +65,8 @@ public:
 
         Value const& value() const { return m_value; }
 
+        [[nodiscard]] bool operator==(CalculationResult const&) const = default;
+
     private:
         void add_or_subtract_internal(SumOperation op, CalculationResult const& other, Optional<Length::ResolutionContext const&>, PercentageBasis const& percentage_basis);
         Value m_value;
@@ -253,6 +255,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) = 0;
 
     virtual void dump(StringBuilder&, int indent) const = 0;
+    virtual bool equals(CalculationNode const&) const = 0;
 
 protected:
     explicit CalculationNode(Type);
@@ -274,6 +277,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override { }
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit NumericCalculationNode(NumericValue);
@@ -293,6 +297,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit SumCalculationNode(Vector<NonnullOwnPtr<CalculationNode>>);
@@ -312,6 +317,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit ProductCalculationNode(Vector<NonnullOwnPtr<CalculationNode>>);
@@ -331,6 +337,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit NegateCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -350,6 +357,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit InvertCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -369,6 +377,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit MinCalculationNode(Vector<NonnullOwnPtr<CalculationNode>>);
@@ -388,6 +397,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit MaxCalculationNode(Vector<NonnullOwnPtr<CalculationNode>>);
@@ -407,6 +417,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit ClampCalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -428,6 +439,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     AbsCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -447,6 +459,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     SignCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -466,6 +479,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override { }
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     ConstantCalculationNode(ConstantType);
@@ -485,6 +499,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     SinCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -504,6 +519,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     CosCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -523,6 +539,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     TanCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -542,6 +559,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     AsinCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -561,6 +579,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     AcosCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -580,6 +599,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     AtanCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -599,6 +619,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     Atan2CalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -619,6 +640,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit PowCalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -639,6 +661,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     SqrtCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -658,6 +681,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     explicit HypotCalculationNode(Vector<NonnullOwnPtr<CalculationNode>>);
@@ -677,6 +701,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     LogCalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -697,6 +722,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     ExpCalculationNode(NonnullOwnPtr<CalculationNode>);
@@ -716,6 +742,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     RoundCalculationNode(RoundingStrategy, NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -737,6 +764,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     ModCalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);
@@ -757,6 +785,7 @@ public:
     virtual void for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const&) override;
 
     virtual void dump(StringBuilder&, int indent) const override;
+    virtual bool equals(CalculationNode const&) const override;
 
 private:
     RemCalculationNode(NonnullOwnPtr<CalculationNode>, NonnullOwnPtr<CalculationNode>);