Create abstract class CXFA_FMChainableExpression

Create the abstract class CXFA_FMChainableExpression, which is derived
from CXFA_FMSimpleExpression, as a base class for expression classes
that chain two CXFA_FMSimpleExpression objects.

This change sets up another change that would allow for space-efficient
destruction of chainable expressions.

Bug: chromium:1052724
Change-Id: I47d015e4c458611261a95920b2dbd3d6b163bcfa
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/67090
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Daniel Hosseinian <dhoss@chromium.org>
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
index e94f55a..ac91d94 100644
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
+++ b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.cpp
@@ -70,12 +70,35 @@
 
 }  // namespace
 
-CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op) : m_op(op) {}
+CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op)
+    : m_op(op), m_bChainable(false) {}
+
+CXFA_FMSimpleExpression::CXFA_FMSimpleExpression(XFA_FM_TOKEN op,
+                                                 bool chainable)
+    : m_op(op), m_bChainable(chainable) {}
 
 XFA_FM_TOKEN CXFA_FMSimpleExpression::GetOperatorToken() const {
   return m_op;
 }
 
+CXFA_FMChainableExpression::CXFA_FMChainableExpression(
+    XFA_FM_TOKEN op,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+    std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
+    : CXFA_FMSimpleExpression(op, /*chainable=*/true),
+      m_pExp1(std::move(pExp1)),
+      m_pExp2(std::move(pExp2)) {}
+
+CXFA_FMChainableExpression::~CXFA_FMChainableExpression() = default;
+
+CXFA_FMSimpleExpression* CXFA_FMChainableExpression::GetFirstExpression() {
+  return m_pExp1.get();
+}
+
+CXFA_FMSimpleExpression* CXFA_FMChainableExpression::GetSecondExpression() {
+  return m_pExp2.get();
+}
+
 CXFA_FMNullExpression::CXFA_FMNullExpression()
     : CXFA_FMSimpleExpression(TOKnull) {}
 
@@ -182,9 +205,7 @@
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMSimpleExpression(op),
-      m_pExp1(std::move(pExp1)),
-      m_pExp2(std::move(pExp2)) {}
+    : CXFA_FMChainableExpression(op, std::move(pExp1), std::move(pExp2)) {}
 
 CXFA_FMAssignExpression::~CXFA_FMAssignExpression() = default;
 
@@ -195,7 +216,8 @@
     return false;
 
   CFX_WideTextBuf tempExp1;
-  if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
+  CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
+  if (!exp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
     return false;
 
   *js << "if (pfm_rt.is_obj(" << tempExp1 << "))\n{\n";
@@ -203,12 +225,13 @@
     *js << "pfm_ret = ";
 
   CFX_WideTextBuf tempExp2;
-  if (!m_pExp2->ToJavaScript(&tempExp2, ReturnType::kInfered))
+  CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
+  if (!exp2->ToJavaScript(&tempExp2, ReturnType::kInfered))
     return false;
 
   *js << "pfm_rt.asgn_val_op(" << tempExp1 << ", " << tempExp2 << ");\n}\n";
 
-  if (m_pExp1->GetOperatorToken() == TOKidentifier &&
+  if (exp1->GetOperatorToken() == TOKidentifier &&
       !tempExp1.AsStringView().EqualsASCII("this")) {
     *js << "else\n{\n";
     if (type == ReturnType::kImplied)
@@ -226,10 +249,8 @@
     XFA_FM_TOKEN op,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pExp2)
-    : CXFA_FMSimpleExpression(op),
-      m_OpName(opName),
-      m_pExp1(std::move(pExp1)),
-      m_pExp2(std::move(pExp2)) {}
+    : CXFA_FMChainableExpression(op, std::move(pExp1), std::move(pExp2)),
+      m_OpName(opName) {}
 
 CXFA_FMBinExpression::~CXFA_FMBinExpression() = default;
 
@@ -239,10 +260,10 @@
     return false;
 
   *js << "pfm_rt." << m_OpName << "(";
-  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
+  if (!GetFirstExpression()->ToJavaScript(js, ReturnType::kInfered))
     return false;
   *js << ", ";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
+  if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInfered))
     return false;
   *js << ")";
   return !CXFA_IsTooBig(js);
@@ -530,10 +551,10 @@
     XFA_FM_TOKEN op,
     WideStringView wsIdentifier,
     std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMSimpleExpression(op),
-      m_wsIdentifier(wsIdentifier),
-      m_pExp1(std::move(pAccessor)),
-      m_pExp2(std::move(pIndexExp)) {}
+    : CXFA_FMChainableExpression(op,
+                                 std::move(pAccessor),
+                                 std::move(pIndexExp)),
+      m_wsIdentifier(wsIdentifier) {}
 
 CXFA_FMDotAccessorExpression::~CXFA_FMDotAccessorExpression() = default;
 
@@ -546,8 +567,9 @@
   *js << "pfm_rt.dot_acc(";
 
   CFX_WideTextBuf tempExp1;
-  if (m_pExp1) {
-    if (!m_pExp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
+  CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
+  if (exp1) {
+    if (!exp1->ToJavaScript(&tempExp1, ReturnType::kInfered))
       return false;
 
     *js << tempExp1;
@@ -556,7 +578,7 @@
   }
   *js << ", \"";
 
-  if (m_pExp1 && m_pExp1->GetOperatorToken() == TOKidentifier)
+  if (exp1 && exp1->GetOperatorToken() == TOKidentifier)
     *js << tempExp1;
 
   *js << "\", ";
@@ -569,7 +591,8 @@
   else
     *js << "\"" << m_wsIdentifier << "\", ";
 
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
+  CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
+  if (!exp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
 
   *js << ")";
@@ -627,10 +650,10 @@
     XFA_FM_TOKEN op,
     WideStringView wsIdentifier,
     std::unique_ptr<CXFA_FMSimpleExpression> pIndexExp)
-    : CXFA_FMSimpleExpression(op),
-      m_wsIdentifier(wsIdentifier),
-      m_pExp1(std::move(pAccessor)),
-      m_pExp2(std::move(pIndexExp)) {}
+    : CXFA_FMChainableExpression(op,
+                                 std::move(pAccessor),
+                                 std::move(pIndexExp)),
+      m_wsIdentifier(wsIdentifier) {}
 
 CXFA_FMDotDotAccessorExpression::~CXFA_FMDotDotAccessorExpression() = default;
 
@@ -640,18 +663,20 @@
   if (CXFA_IsTooBig(js) || !depthManager.IsWithinMaxDepth())
     return false;
 
+  CXFA_FMSimpleExpression* exp1 = GetFirstExpression();
   *js << "pfm_rt.dotdot_acc(";
-  if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
+  if (!exp1->ToJavaScript(js, ReturnType::kInfered))
     return false;
   *js << ", "
       << "\"";
-  if (m_pExp1->GetOperatorToken() == TOKidentifier) {
-    if (!m_pExp1->ToJavaScript(js, ReturnType::kInfered))
+  if (exp1->GetOperatorToken() == TOKidentifier) {
+    if (!exp1->ToJavaScript(js, ReturnType::kInfered))
       return false;
   }
 
+  CXFA_FMSimpleExpression* exp2 = GetSecondExpression();
   *js << "\", \"" << m_wsIdentifier << "\", ";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
+  if (!exp2->ToJavaScript(js, ReturnType::kInfered))
     return false;
   *js << ")";
   return !CXFA_IsTooBig(js);
@@ -660,9 +685,9 @@
 CXFA_FMMethodCallExpression::CXFA_FMMethodCallExpression(
     std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
     std::unique_ptr<CXFA_FMSimpleExpression> pCallExp)
-    : CXFA_FMSimpleExpression(TOKdot),
-      m_pExp1(std::move(pAccessorExp1)),
-      m_pExp2(std::move(pCallExp)) {}
+    : CXFA_FMChainableExpression(TOKdot,
+                                 std::move(pAccessorExp1),
+                                 std::move(pCallExp)) {}
 
 CXFA_FMMethodCallExpression::~CXFA_FMMethodCallExpression() = default;
 
@@ -673,13 +698,13 @@
     return false;
 
   CFX_WideTextBuf buf;
-  if (!m_pExp1->ToJavaScript(&buf, ReturnType::kInfered))
+  if (!GetFirstExpression()->ToJavaScript(&buf, ReturnType::kInfered))
     return false;
 
   *js << "(function() {\n";
   *js << "  return pfm_method_runner(" << buf << ", function(obj) {\n";
   *js << "    return obj.";
-  if (!m_pExp2->ToJavaScript(js, ReturnType::kInfered))
+  if (!GetSecondExpression()->ToJavaScript(js, ReturnType::kInfered))
     return false;
   *js << ";\n";
   *js << "  });\n";
diff --git a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
index ae8f38e..49e2560 100644
--- a/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
+++ b/xfa/fxfa/fm2js/cxfa_fmsimpleexpression.h
@@ -29,11 +29,33 @@
   virtual bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) = 0;
 
   XFA_FM_TOKEN GetOperatorToken() const;
+  bool chainable() const { return m_bChainable; }
 
  protected:
   explicit CXFA_FMSimpleExpression(XFA_FM_TOKEN op);
+  CXFA_FMSimpleExpression(XFA_FM_TOKEN op, bool chainable);
 
   const XFA_FM_TOKEN m_op;
+
+ private:
+  const bool m_bChainable;
+};
+
+class CXFA_FMChainableExpression : public CXFA_FMSimpleExpression {
+ public:
+  ~CXFA_FMChainableExpression() override;
+
+ protected:
+  CXFA_FMChainableExpression(XFA_FM_TOKEN op,
+                             std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
+                             std::unique_ptr<CXFA_FMSimpleExpression> pExp2);
+
+  CXFA_FMSimpleExpression* GetFirstExpression();
+  CXFA_FMSimpleExpression* GetSecondExpression();
+
+ private:
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
+  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
 class CXFA_FMNullExpression final : public CXFA_FMSimpleExpression {
@@ -77,7 +99,7 @@
   WideStringView m_wsIdentifier;
 };
 
-class CXFA_FMAssignExpression final : public CXFA_FMSimpleExpression {
+class CXFA_FMAssignExpression final : public CXFA_FMChainableExpression {
  public:
   CXFA_FMAssignExpression(XFA_FM_TOKEN op,
                           std::unique_ptr<CXFA_FMSimpleExpression> pExp1,
@@ -85,13 +107,9 @@
   ~CXFA_FMAssignExpression() override;
 
   bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMBinExpression : public CXFA_FMSimpleExpression {
+class CXFA_FMBinExpression : public CXFA_FMChainableExpression {
  public:
   ~CXFA_FMBinExpression() override;
 
@@ -105,8 +123,6 @@
 
  private:
   WideString m_OpName;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
 class CXFA_FMLogicalOrExpression final : public CXFA_FMBinExpression {
@@ -257,7 +273,7 @@
   std::vector<std::unique_ptr<CXFA_FMSimpleExpression>> m_Arguments;
 };
 
-class CXFA_FMDotAccessorExpression final : public CXFA_FMSimpleExpression {
+class CXFA_FMDotAccessorExpression final : public CXFA_FMChainableExpression {
  public:
   CXFA_FMDotAccessorExpression(
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
@@ -270,8 +286,6 @@
 
  private:
   WideStringView m_wsIdentifier;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
 class CXFA_FMIndexExpression final : public CXFA_FMSimpleExpression {
@@ -289,7 +303,8 @@
   bool m_bIsStarIndex;
 };
 
-class CXFA_FMDotDotAccessorExpression final : public CXFA_FMSimpleExpression {
+class CXFA_FMDotDotAccessorExpression final
+    : public CXFA_FMChainableExpression {
  public:
   CXFA_FMDotDotAccessorExpression(
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessor,
@@ -302,11 +317,9 @@
 
  private:
   WideStringView m_wsIdentifier;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
-class CXFA_FMMethodCallExpression final : public CXFA_FMSimpleExpression {
+class CXFA_FMMethodCallExpression final : public CXFA_FMChainableExpression {
  public:
   CXFA_FMMethodCallExpression(
       std::unique_ptr<CXFA_FMSimpleExpression> pAccessorExp1,
@@ -314,10 +327,6 @@
   ~CXFA_FMMethodCallExpression() override;
 
   bool ToJavaScript(CFX_WideTextBuf* js, ReturnType type) override;
-
- private:
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp1;
-  std::unique_ptr<CXFA_FMSimpleExpression> m_pExp2;
 };
 
 bool CXFA_IsTooBig(const CFX_WideTextBuf* js);