Don't trust CJX_Objects handed back from JavaScript.

Implement our own dynamic typing to ensure we are not making
bad casts since we don't have RTTI. There are too many ways
that JS can apply methods/getter to objects that this provides
another line of defense.

Put all the type information constants into the header so that
they can be easily checked against the actual class hierarchy.
The changes to the .cpp files should all be boilerplate, except
for CJX_Object, which has no superclass.

Apply the check inside the jse_define.h macros before making cast.

Bug: chromium:922864
Change-Id: I4d5faf572949a72168b39d43d33eea22659194b1
Reviewed-on: https://pdfium-review.googlesource.com/c/48650
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fxjs/xfa/cjx_boolean.cpp b/fxjs/xfa/cjx_boolean.cpp
index efea115..c62f24d 100644
--- a/fxjs/xfa/cjx_boolean.cpp
+++ b/fxjs/xfa/cjx_boolean.cpp
@@ -13,6 +13,10 @@
 
 CJX_Boolean::~CJX_Boolean() = default;
 
+bool CJX_Boolean::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Boolean::defaultValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_boolean.h b/fxjs/xfa/cjx_boolean.h
index 8e90d62..ed1f3dd 100644
--- a/fxjs/xfa/cjx_boolean.h
+++ b/fxjs/xfa/cjx_boolean.h
@@ -17,11 +17,17 @@
   explicit CJX_Boolean(CXFA_Boolean* node);
   ~CJX_Boolean() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Boolean;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Boolean;
 };
 
 #endif  // FXJS_XFA_CJX_BOOLEAN_H_
diff --git a/fxjs/xfa/cjx_comb.cpp b/fxjs/xfa/cjx_comb.cpp
index 8680972..2541822 100644
--- a/fxjs/xfa/cjx_comb.cpp
+++ b/fxjs/xfa/cjx_comb.cpp
@@ -12,6 +12,10 @@
 
 CJX_Comb::~CJX_Comb() = default;
 
+bool CJX_Comb::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Comb::numberOfCells(CFXJSE_Value* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_comb.h b/fxjs/xfa/cjx_comb.h
index b5140ec..d7573ff 100644
--- a/fxjs/xfa/cjx_comb.h
+++ b/fxjs/xfa/cjx_comb.h
@@ -17,10 +17,16 @@
   explicit CJX_Comb(CXFA_Comb* node);
   ~CJX_Comb() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(numberOfCells);
 
  private:
   using Type__ = CJX_Comb;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Comb;
 };
 
 #endif  // FXJS_XFA_CJX_COMB_H_
diff --git a/fxjs/xfa/cjx_container.cpp b/fxjs/xfa/cjx_container.cpp
index 515a5e2..44b1ff8 100644
--- a/fxjs/xfa/cjx_container.cpp
+++ b/fxjs/xfa/cjx_container.cpp
@@ -25,6 +25,10 @@
 
 CJX_Container::~CJX_Container() {}
 
+bool CJX_Container::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Container::getDelta(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_container.h b/fxjs/xfa/cjx_container.h
index 914056a..51675e5 100644
--- a/fxjs/xfa/cjx_container.h
+++ b/fxjs/xfa/cjx_container.h
@@ -17,12 +17,17 @@
   explicit CJX_Container(CXFA_Node* node);
   ~CJX_Container() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(getDelta);
   JSE_METHOD(getDeltas);
 
  private:
   using Type__ = CJX_Container;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Container;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_datavalue.cpp b/fxjs/xfa/cjx_datavalue.cpp
index 7d55fa9..0d92ec4 100644
--- a/fxjs/xfa/cjx_datavalue.cpp
+++ b/fxjs/xfa/cjx_datavalue.cpp
@@ -12,6 +12,10 @@
 
 CJX_DataValue::~CJX_DataValue() = default;
 
+bool CJX_DataValue::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_DataValue::defaultValue(CFXJSE_Value* pValue,
                                  bool bSetting,
                                  XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_datavalue.h b/fxjs/xfa/cjx_datavalue.h
index 6c48d93..3ff1a48 100644
--- a/fxjs/xfa/cjx_datavalue.h
+++ b/fxjs/xfa/cjx_datavalue.h
@@ -17,12 +17,18 @@
   explicit CJX_DataValue(CXFA_DataValue* node);
   ~CJX_DataValue() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(isNull);
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_DataValue;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::DataValue;
 };
 
 #endif  // FXJS_XFA_CJX_DATAVALUE_H_
diff --git a/fxjs/xfa/cjx_datawindow.cpp b/fxjs/xfa/cjx_datawindow.cpp
index 0c7f67d..d165140 100644
--- a/fxjs/xfa/cjx_datawindow.cpp
+++ b/fxjs/xfa/cjx_datawindow.cpp
@@ -24,6 +24,10 @@
 
 CJX_DataWindow::~CJX_DataWindow() {}
 
+bool CJX_DataWindow::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_DataWindow::moveCurrentRecord(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_datawindow.h b/fxjs/xfa/cjx_datawindow.h
index 75146ac..6f3ad54 100644
--- a/fxjs/xfa/cjx_datawindow.h
+++ b/fxjs/xfa/cjx_datawindow.h
@@ -19,6 +19,9 @@
   explicit CJX_DataWindow(CScript_DataWindow* window);
   ~CJX_DataWindow() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(gotoRecord);
   JSE_METHOD(isRecordGroup);
   JSE_METHOD(moveCurrentRecord);
@@ -31,7 +34,9 @@
 
  private:
   using Type__ = CJX_DataWindow;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::DataWindow;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_date.cpp b/fxjs/xfa/cjx_date.cpp
index f55943e..d6db61b 100644
--- a/fxjs/xfa/cjx_date.cpp
+++ b/fxjs/xfa/cjx_date.cpp
@@ -12,6 +12,10 @@
 
 CJX_Date::~CJX_Date() = default;
 
+bool CJX_Date::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Date::defaultValue(CFXJSE_Value* pValue,
                             bool bSetting,
                             XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_date.h b/fxjs/xfa/cjx_date.h
index 52c4349..e151fcc 100644
--- a/fxjs/xfa/cjx_date.h
+++ b/fxjs/xfa/cjx_date.h
@@ -17,11 +17,17 @@
   explicit CJX_Date(CXFA_Date* node);
   ~CJX_Date() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Date;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Date;
 };
 
 #endif  // FXJS_XFA_CJX_DATE_H_
diff --git a/fxjs/xfa/cjx_datetime.cpp b/fxjs/xfa/cjx_datetime.cpp
index 63f097c..252386f 100644
--- a/fxjs/xfa/cjx_datetime.cpp
+++ b/fxjs/xfa/cjx_datetime.cpp
@@ -12,6 +12,10 @@
 
 CJX_DateTime::~CJX_DateTime() = default;
 
+bool CJX_DateTime::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_DateTime::defaultValue(CFXJSE_Value* pValue,
                                 bool bSetting,
                                 XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_datetime.h b/fxjs/xfa/cjx_datetime.h
index df22909..a070fe5 100644
--- a/fxjs/xfa/cjx_datetime.h
+++ b/fxjs/xfa/cjx_datetime.h
@@ -17,11 +17,17 @@
   explicit CJX_DateTime(CXFA_DateTime* node);
   ~CJX_DateTime() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_DateTime;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::DateTime;
 };
 
 #endif  // FXJS_XFA_CJX_DATETIME_H_
diff --git a/fxjs/xfa/cjx_decimal.cpp b/fxjs/xfa/cjx_decimal.cpp
index 14c77f1..20a5de7 100644
--- a/fxjs/xfa/cjx_decimal.cpp
+++ b/fxjs/xfa/cjx_decimal.cpp
@@ -12,6 +12,10 @@
 
 CJX_Decimal::~CJX_Decimal() = default;
 
+bool CJX_Decimal::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Decimal::defaultValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_decimal.h b/fxjs/xfa/cjx_decimal.h
index 8330a0e..59b5153 100644
--- a/fxjs/xfa/cjx_decimal.h
+++ b/fxjs/xfa/cjx_decimal.h
@@ -17,11 +17,17 @@
   explicit CJX_Decimal(CXFA_Decimal* node);
   ~CJX_Decimal() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Decimal;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Decimal;
 };
 
 #endif  // FXJS_XFA_CJX_DECIMAL_H_
diff --git a/fxjs/xfa/cjx_delta.cpp b/fxjs/xfa/cjx_delta.cpp
index 5622bf9..3ad20a6 100644
--- a/fxjs/xfa/cjx_delta.cpp
+++ b/fxjs/xfa/cjx_delta.cpp
@@ -20,6 +20,10 @@
 
 CJX_Delta::~CJX_Delta() {}
 
+bool CJX_Delta::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Delta::restore(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
diff --git a/fxjs/xfa/cjx_delta.h b/fxjs/xfa/cjx_delta.h
index 0a358fd..716dd1e 100644
--- a/fxjs/xfa/cjx_delta.h
+++ b/fxjs/xfa/cjx_delta.h
@@ -17,6 +17,9 @@
   explicit CJX_Delta(CXFA_Delta* delta);
   ~CJX_Delta() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(restore);
 
   JSE_PROP(currentValue);
@@ -25,7 +28,9 @@
 
  private:
   using Type__ = CJX_Delta;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::Delta;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_desc.cpp b/fxjs/xfa/cjx_desc.cpp
index b54a7ba..2bbff2b 100644
--- a/fxjs/xfa/cjx_desc.cpp
+++ b/fxjs/xfa/cjx_desc.cpp
@@ -21,6 +21,10 @@
 
 CJX_Desc::~CJX_Desc() {}
 
+bool CJX_Desc::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Desc::metadata(CFX_V8* runtime,
                               const std::vector<v8::Local<v8::Value>>& params) {
   if (params.size() != 0 && params.size() != 1)
diff --git a/fxjs/xfa/cjx_desc.h b/fxjs/xfa/cjx_desc.h
index 1e71f65..62cdec8 100644
--- a/fxjs/xfa/cjx_desc.h
+++ b/fxjs/xfa/cjx_desc.h
@@ -17,11 +17,16 @@
   explicit CJX_Desc(CXFA_Desc* desc);
   ~CJX_Desc() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(metadata);
 
  private:
   using Type__ = CJX_Desc;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Desc;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_draw.cpp b/fxjs/xfa/cjx_draw.cpp
index 9fae8f9..4e8ddc1 100644
--- a/fxjs/xfa/cjx_draw.cpp
+++ b/fxjs/xfa/cjx_draw.cpp
@@ -12,6 +12,11 @@
 CJX_Draw::CJX_Draw(CXFA_Draw* node) : CJX_Container(node) {}
 
 CJX_Draw::~CJX_Draw() = default;
+
+bool CJX_Draw::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Draw::rawValue(CFXJSE_Value* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_draw.h b/fxjs/xfa/cjx_draw.h
index 134a1a0..bf3c3a7 100644
--- a/fxjs/xfa/cjx_draw.h
+++ b/fxjs/xfa/cjx_draw.h
@@ -17,11 +17,17 @@
   explicit CJX_Draw(CXFA_Draw* node);
   ~CJX_Draw() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(rawValue);
 
  private:
   using Type__ = CJX_Draw;
+  using ParentType__ = CJX_Container;
+
+  static const TypeTag static_type__ = TypeTag::Draw;
 };
 
 #endif  // FXJS_XFA_CJX_DRAW_H_
diff --git a/fxjs/xfa/cjx_encrypt.cpp b/fxjs/xfa/cjx_encrypt.cpp
index fc0ed4c..f50133d 100644
--- a/fxjs/xfa/cjx_encrypt.cpp
+++ b/fxjs/xfa/cjx_encrypt.cpp
@@ -12,6 +12,10 @@
 
 CJX_Encrypt::~CJX_Encrypt() = default;
 
+bool CJX_Encrypt::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Encrypt::format(CFXJSE_Value* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_encrypt.h b/fxjs/xfa/cjx_encrypt.h
index 1bf4fe9..826bc1f 100644
--- a/fxjs/xfa/cjx_encrypt.h
+++ b/fxjs/xfa/cjx_encrypt.h
@@ -17,10 +17,16 @@
   explicit CJX_Encrypt(CXFA_Encrypt* node);
   ~CJX_Encrypt() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(format);
 
  private:
   using Type__ = CJX_Encrypt;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Encrypt;
 };
 
 #endif  // FXJS_XFA_CJX_ENCRYPT_H_
diff --git a/fxjs/xfa/cjx_eventpseudomodel.cpp b/fxjs/xfa/cjx_eventpseudomodel.cpp
index b8ef6ea..99c155e 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.cpp
+++ b/fxjs/xfa/cjx_eventpseudomodel.cpp
@@ -56,6 +56,10 @@
 
 CJX_EventPseudoModel::~CJX_EventPseudoModel() {}
 
+bool CJX_EventPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_EventPseudoModel::cancelAction(CFXJSE_Value* pValue,
                                         bool bSetting,
                                         XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_eventpseudomodel.h b/fxjs/xfa/cjx_eventpseudomodel.h
index 6d13018..6f3cc84 100644
--- a/fxjs/xfa/cjx_eventpseudomodel.h
+++ b/fxjs/xfa/cjx_eventpseudomodel.h
@@ -38,6 +38,9 @@
   explicit CJX_EventPseudoModel(CScript_EventPseudoModel* model);
   ~CJX_EventPseudoModel() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(emit);
   JSE_METHOD(reset);
 
@@ -61,10 +64,12 @@
 
  private:
   using Type__ = CJX_EventPseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::EventPseudoModel;
+  static const CJX_MethodSpec MethodSpecs[];
 
   void Property(CFXJSE_Value* pValue, XFA_Event dwFlag, bool bSetting);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_EVENTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_exclgroup.cpp b/fxjs/xfa/cjx_exclgroup.cpp
index 5d71f0d..d2ed193 100644
--- a/fxjs/xfa/cjx_exclgroup.cpp
+++ b/fxjs/xfa/cjx_exclgroup.cpp
@@ -30,6 +30,10 @@
 
 CJX_ExclGroup::~CJX_ExclGroup() {}
 
+bool CJX_ExclGroup::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_ExclGroup::execEvent(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_exclgroup.h b/fxjs/xfa/cjx_exclgroup.h
index f57fa86..0fa3fc6 100644
--- a/fxjs/xfa/cjx_exclgroup.h
+++ b/fxjs/xfa/cjx_exclgroup.h
@@ -17,6 +17,9 @@
   explicit CJX_ExclGroup(CXFA_ExclGroup* group);
   ~CJX_ExclGroup() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(execCalculate);
   JSE_METHOD(execEvent);
   JSE_METHOD(execInitialize);
@@ -35,7 +38,9 @@
 
  private:
   using Type__ = CJX_ExclGroup;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::ExclGroup;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_exdata.cpp b/fxjs/xfa/cjx_exdata.cpp
index 6ea3877..45f6d81 100644
--- a/fxjs/xfa/cjx_exdata.cpp
+++ b/fxjs/xfa/cjx_exdata.cpp
@@ -12,6 +12,10 @@
 
 CJX_ExData::~CJX_ExData() = default;
 
+bool CJX_ExData::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_ExData::defaultValue(CFXJSE_Value* pValue,
                               bool bSetting,
                               XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_exdata.h b/fxjs/xfa/cjx_exdata.h
index 86d9219..7d504ea 100644
--- a/fxjs/xfa/cjx_exdata.h
+++ b/fxjs/xfa/cjx_exdata.h
@@ -17,10 +17,16 @@
   explicit CJX_ExData(CXFA_ExData* node);
   ~CJX_ExData() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
 
  private:
   using Type__ = CJX_ExData;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::ExData;
 };
 
 #endif  // FXJS_XFA_CJX_EXDATA_H_
diff --git a/fxjs/xfa/cjx_extras.cpp b/fxjs/xfa/cjx_extras.cpp
index e68c879..a2e2eea 100644
--- a/fxjs/xfa/cjx_extras.cpp
+++ b/fxjs/xfa/cjx_extras.cpp
@@ -12,6 +12,10 @@
 
 CJX_Extras::~CJX_Extras() = default;
 
+bool CJX_Extras::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Extras::type(CFXJSE_Value* pValue,
                       bool bSetting,
                       XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_extras.h b/fxjs/xfa/cjx_extras.h
index 420bfe4..0723575 100644
--- a/fxjs/xfa/cjx_extras.h
+++ b/fxjs/xfa/cjx_extras.h
@@ -17,10 +17,16 @@
   explicit CJX_Extras(CXFA_Extras* node);
   ~CJX_Extras() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(type);
 
  private:
   using Type__ = CJX_Extras;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Extras;
 };
 
 #endif  // FXJS_XFA_CJX_EXTRAS_H_
diff --git a/fxjs/xfa/cjx_field.cpp b/fxjs/xfa/cjx_field.cpp
index 2bc5924..7f0d05f 100644
--- a/fxjs/xfa/cjx_field.cpp
+++ b/fxjs/xfa/cjx_field.cpp
@@ -39,6 +39,10 @@
 
 CJX_Field::~CJX_Field() {}
 
+bool CJX_Field::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Field::clearItems(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_field.h b/fxjs/xfa/cjx_field.h
index 72ded92..182c592 100644
--- a/fxjs/xfa/cjx_field.h
+++ b/fxjs/xfa/cjx_field.h
@@ -17,6 +17,9 @@
   explicit CJX_Field(CXFA_Field* field);
   ~CJX_Field() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(addItem);
   JSE_METHOD(boundItem);
   JSE_METHOD(clearItems);
@@ -47,7 +50,9 @@
 
  private:
   using Type__ = CJX_Field;
+  using ParentType__ = CJX_Container;
 
+  static const TypeTag static_type__ = TypeTag::Field;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_float.cpp b/fxjs/xfa/cjx_float.cpp
index 0300999..5223f9f 100644
--- a/fxjs/xfa/cjx_float.cpp
+++ b/fxjs/xfa/cjx_float.cpp
@@ -12,6 +12,10 @@
 
 CJX_Float::~CJX_Float() = default;
 
+bool CJX_Float::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Float::defaultValue(CFXJSE_Value* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_float.h b/fxjs/xfa/cjx_float.h
index c964339..473d8d6 100644
--- a/fxjs/xfa/cjx_float.h
+++ b/fxjs/xfa/cjx_float.h
@@ -17,11 +17,17 @@
   explicit CJX_Float(CXFA_Float* node);
   ~CJX_Float() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Float;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Float;
 };
 
 #endif  // FXJS_XFA_CJX_FLOAT_H_
diff --git a/fxjs/xfa/cjx_form.cpp b/fxjs/xfa/cjx_form.cpp
index 3004866..c67688b 100644
--- a/fxjs/xfa/cjx_form.cpp
+++ b/fxjs/xfa/cjx_form.cpp
@@ -31,6 +31,10 @@
 
 CJX_Form::~CJX_Form() {}
 
+bool CJX_Form::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Form::formNodes(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_form.h b/fxjs/xfa/cjx_form.h
index f477a1c..0968c55 100644
--- a/fxjs/xfa/cjx_form.h
+++ b/fxjs/xfa/cjx_form.h
@@ -17,6 +17,9 @@
   explicit CJX_Form(CXFA_Form* form);
   ~CJX_Form() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(execCalculate);
   JSE_METHOD(execInitialize);
   JSE_METHOD(execValidate);
@@ -26,7 +29,9 @@
 
  private:
   using Type__ = CJX_Form;
+  using ParentType__ = CJX_Model;
 
+  static const TypeTag static_type__ = TypeTag::Form;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_handler.cpp b/fxjs/xfa/cjx_handler.cpp
index fb7b464..21be88c 100644
--- a/fxjs/xfa/cjx_handler.cpp
+++ b/fxjs/xfa/cjx_handler.cpp
@@ -12,6 +12,10 @@
 
 CJX_Handler::~CJX_Handler() = default;
 
+bool CJX_Handler::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Handler::version(CFXJSE_Value* pValue,
                           bool bSetting,
                           XFA_Attribute eAttribute) {}
diff --git a/fxjs/xfa/cjx_handler.h b/fxjs/xfa/cjx_handler.h
index a5ad537..348eaf1 100644
--- a/fxjs/xfa/cjx_handler.h
+++ b/fxjs/xfa/cjx_handler.h
@@ -17,10 +17,16 @@
   explicit CJX_Handler(CXFA_Handler* node);
   ~CJX_Handler() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(version);
 
  private:
   using Type__ = CJX_Handler;
+  using ParentType__ = CJX_TextNode;
+
+  static const TypeTag static_type__ = TypeTag::Handler;
 };
 
 #endif  // FXJS_XFA_CJX_HANDLER_H_
diff --git a/fxjs/xfa/cjx_hostpseudomodel.cpp b/fxjs/xfa/cjx_hostpseudomodel.cpp
index 1eeaffa..2e7d137 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.cpp
+++ b/fxjs/xfa/cjx_hostpseudomodel.cpp
@@ -73,6 +73,10 @@
 
 CJX_HostPseudoModel::~CJX_HostPseudoModel() {}
 
+bool CJX_HostPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_HostPseudoModel::appType(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_hostpseudomodel.h b/fxjs/xfa/cjx_hostpseudomodel.h
index 956a2fa..cdc293d 100644
--- a/fxjs/xfa/cjx_hostpseudomodel.h
+++ b/fxjs/xfa/cjx_hostpseudomodel.h
@@ -19,6 +19,9 @@
   explicit CJX_HostPseudoModel(CScript_HostPseudoModel* model);
   ~CJX_HostPseudoModel() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(beep);
   JSE_METHOD(documentCountInBatch);
   JSE_METHOD(documentInBatch);
@@ -52,7 +55,9 @@
 
  private:
   using Type__ = CJX_HostPseudoModel;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::HostPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_image.cpp b/fxjs/xfa/cjx_image.cpp
index 523d7e3..794f353 100644
--- a/fxjs/xfa/cjx_image.cpp
+++ b/fxjs/xfa/cjx_image.cpp
@@ -12,6 +12,10 @@
 
 CJX_Image::~CJX_Image() = default;
 
+bool CJX_Image::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Image::defaultValue(CFXJSE_Value* pValue,
                              bool bSetting,
                              XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_image.h b/fxjs/xfa/cjx_image.h
index fa0aa8d..ad9a113 100644
--- a/fxjs/xfa/cjx_image.h
+++ b/fxjs/xfa/cjx_image.h
@@ -17,11 +17,17 @@
   explicit CJX_Image(CXFA_Image* node);
   ~CJX_Image() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Image;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Image;
 };
 
 #endif  // FXJS_XFA_CJX_IMAGE_H_
diff --git a/fxjs/xfa/cjx_instancemanager.cpp b/fxjs/xfa/cjx_instancemanager.cpp
index 4da4945..a4be9e1 100644
--- a/fxjs/xfa/cjx_instancemanager.cpp
+++ b/fxjs/xfa/cjx_instancemanager.cpp
@@ -33,6 +33,10 @@
 
 CJX_InstanceManager::~CJX_InstanceManager() {}
 
+bool CJX_InstanceManager::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 int32_t CJX_InstanceManager::SetInstances(int32_t iDesired) {
   CXFA_Occur* occur = GetXFANode()->GetOccurIfExists();
   int32_t iMin = occur ? occur->GetMin() : CXFA_Occur::kDefaultMin;
diff --git a/fxjs/xfa/cjx_instancemanager.h b/fxjs/xfa/cjx_instancemanager.h
index 5568232..3fae9ab 100644
--- a/fxjs/xfa/cjx_instancemanager.h
+++ b/fxjs/xfa/cjx_instancemanager.h
@@ -17,6 +17,9 @@
   explicit CJX_InstanceManager(CXFA_InstanceManager* mgr);
   ~CJX_InstanceManager() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(addInstance);
   JSE_METHOD(insertInstance);
   JSE_METHOD(moveInstance);
@@ -31,10 +34,12 @@
 
  private:
   using Type__ = CJX_InstanceManager;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::InstanceManager;
+  static const CJX_MethodSpec MethodSpecs[];
 
   int32_t SetInstances(int32_t iDesired);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_INSTANCEMANAGER_H_
diff --git a/fxjs/xfa/cjx_integer.cpp b/fxjs/xfa/cjx_integer.cpp
index 34c108a..08aac94 100644
--- a/fxjs/xfa/cjx_integer.cpp
+++ b/fxjs/xfa/cjx_integer.cpp
@@ -12,6 +12,10 @@
 
 CJX_Integer::~CJX_Integer() = default;
 
+bool CJX_Integer::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Integer::defaultValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_integer.h b/fxjs/xfa/cjx_integer.h
index 608c8fd..06b997c 100644
--- a/fxjs/xfa/cjx_integer.h
+++ b/fxjs/xfa/cjx_integer.h
@@ -17,11 +17,17 @@
   explicit CJX_Integer(CXFA_Integer* node);
   ~CJX_Integer() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Integer;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Integer;
 };
 
 #endif  // FXJS_XFA_CJX_INTEGER_H_
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.cpp b/fxjs/xfa/cjx_layoutpseudomodel.cpp
index 2342981..7ade3b4 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.cpp
+++ b/fxjs/xfa/cjx_layoutpseudomodel.cpp
@@ -57,6 +57,10 @@
 
 CJX_LayoutPseudoModel::~CJX_LayoutPseudoModel() {}
 
+bool CJX_LayoutPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_LayoutPseudoModel::ready(CFXJSE_Value* pValue,
                                   bool bSetting,
                                   XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_layoutpseudomodel.h b/fxjs/xfa/cjx_layoutpseudomodel.h
index cf2850f..d5f0cba 100644
--- a/fxjs/xfa/cjx_layoutpseudomodel.h
+++ b/fxjs/xfa/cjx_layoutpseudomodel.h
@@ -29,6 +29,9 @@
   explicit CJX_LayoutPseudoModel(CScript_LayoutPseudoModel* model);
   ~CJX_LayoutPseudoModel() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(absPage);
   JSE_METHOD(absPageCount);
   JSE_METHOD(absPageCountInBatch);
@@ -53,6 +56,10 @@
 
  private:
   using Type__ = CJX_LayoutPseudoModel;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::LayoutPseudoModel;
+  static const CJX_MethodSpec MethodSpecs[];
 
   CJS_Result NumberedPageCount(CFX_V8* runtime, bool bNumbered);
   CJS_Result HWXY(CFX_V8* runtime,
@@ -65,8 +72,6 @@
   CJS_Result PageInternals(CFX_V8* runtime,
                            const std::vector<v8::Local<v8::Value>>& params,
                            bool bAbsPage);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_LAYOUTPSEUDOMODEL_H_
diff --git a/fxjs/xfa/cjx_list.cpp b/fxjs/xfa/cjx_list.cpp
index 3c165e6..28eb215 100644
--- a/fxjs/xfa/cjx_list.cpp
+++ b/fxjs/xfa/cjx_list.cpp
@@ -28,6 +28,10 @@
 
 CJX_List::~CJX_List() {}
 
+bool CJX_List::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CXFA_List* CJX_List::GetXFAList() {
   return ToList(GetXFAObject());
 }
diff --git a/fxjs/xfa/cjx_list.h b/fxjs/xfa/cjx_list.h
index c0cfecd..842b859 100644
--- a/fxjs/xfa/cjx_list.h
+++ b/fxjs/xfa/cjx_list.h
@@ -17,6 +17,9 @@
   explicit CJX_List(CXFA_List* list);
   ~CJX_List() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(append);
   JSE_METHOD(insert);
   JSE_METHOD(item);
@@ -26,10 +29,12 @@
 
  private:
   using Type__ = CJX_List;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::List;
+  static const CJX_MethodSpec MethodSpecs[];
 
   CXFA_List* GetXFAList();
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_LIST_H_
diff --git a/fxjs/xfa/cjx_logpseudomodel.cpp b/fxjs/xfa/cjx_logpseudomodel.cpp
index b1c1f1c..8cb9ee7 100644
--- a/fxjs/xfa/cjx_logpseudomodel.cpp
+++ b/fxjs/xfa/cjx_logpseudomodel.cpp
@@ -25,6 +25,10 @@
 
 CJX_LogPseudoModel::~CJX_LogPseudoModel() {}
 
+bool CJX_LogPseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_LogPseudoModel::message(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_logpseudomodel.h b/fxjs/xfa/cjx_logpseudomodel.h
index 876917e..fda3bb9 100644
--- a/fxjs/xfa/cjx_logpseudomodel.h
+++ b/fxjs/xfa/cjx_logpseudomodel.h
@@ -19,6 +19,9 @@
   explicit CJX_LogPseudoModel(CScript_LogPseudoModel* model);
   ~CJX_LogPseudoModel() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(message);
   JSE_METHOD(traceEnabled);
   JSE_METHOD(traceActivate);
@@ -27,7 +30,9 @@
 
  private:
   using Type__ = CJX_LogPseudoModel;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::LogPseudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_manifest.cpp b/fxjs/xfa/cjx_manifest.cpp
index f3837a5..30c18b7 100644
--- a/fxjs/xfa/cjx_manifest.cpp
+++ b/fxjs/xfa/cjx_manifest.cpp
@@ -22,6 +22,10 @@
 
 CJX_Manifest::~CJX_Manifest() {}
 
+bool CJX_Manifest::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Manifest::evaluate(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_manifest.h b/fxjs/xfa/cjx_manifest.h
index 2d81576..f441689 100644
--- a/fxjs/xfa/cjx_manifest.h
+++ b/fxjs/xfa/cjx_manifest.h
@@ -17,13 +17,18 @@
   explicit CJX_Manifest(CXFA_Manifest* manifest);
   ~CJX_Manifest() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(evaluate);
 
   JSE_PROP(defaultValue); /* {default} */
 
  private:
   using Type__ = CJX_Manifest;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Manifest;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_model.cpp b/fxjs/xfa/cjx_model.cpp
index f72fbc4..66ae1ea 100644
--- a/fxjs/xfa/cjx_model.cpp
+++ b/fxjs/xfa/cjx_model.cpp
@@ -26,6 +26,10 @@
 
 CJX_Model::~CJX_Model() {}
 
+bool CJX_Model::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Model::clearErrorList(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_model.h b/fxjs/xfa/cjx_model.h
index b6b92f3..9fd54f5 100644
--- a/fxjs/xfa/cjx_model.h
+++ b/fxjs/xfa/cjx_model.h
@@ -17,6 +17,9 @@
   explicit CJX_Model(CXFA_Node* obj);
   ~CJX_Model() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(clearErrorList);
   JSE_METHOD(createNode);
   JSE_METHOD(isCompatibleNS);
@@ -26,7 +29,9 @@
 
  private:
   using Type__ = CJX_Model;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Model;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_node.cpp b/fxjs/xfa/cjx_node.cpp
index f7fac81..4997f23 100644
--- a/fxjs/xfa/cjx_node.cpp
+++ b/fxjs/xfa/cjx_node.cpp
@@ -149,6 +149,10 @@
 
 CJX_Node::~CJX_Node() = default;
 
+bool CJX_Node::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CXFA_Node* CJX_Node::GetXFANode() const {
   return ToNode(GetXFAObject());
 }
diff --git a/fxjs/xfa/cjx_node.h b/fxjs/xfa/cjx_node.h
index d26e7a0..29b7c8e 100644
--- a/fxjs/xfa/cjx_node.h
+++ b/fxjs/xfa/cjx_node.h
@@ -18,6 +18,9 @@
   explicit CJX_Node(CXFA_Node* node);
   ~CJX_Node() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(applyXSL);
   JSE_METHOD(assignNode);
   JSE_METHOD(clone);
@@ -43,7 +46,9 @@
 
  private:
   using Type__ = CJX_Node;
+  using ParentType__ = CJX_Tree;
 
+  static const TypeTag static_type__ = TypeTag::Node;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_object.cpp b/fxjs/xfa/cjx_object.cpp
index 9feab1b..eee98f0 100644
--- a/fxjs/xfa/cjx_object.cpp
+++ b/fxjs/xfa/cjx_object.cpp
@@ -132,6 +132,10 @@
   ClearMapModuleBuffer();
 }
 
+bool CJX_Object::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__;
+}
+
 void CJX_Object::DefineMethods(pdfium::span<const CJX_MethodSpec> methods) {
   for (const auto& item : methods)
     method_specs_[item.pName] = item.pMethodCall;
diff --git a/fxjs/xfa/cjx_object.h b/fxjs/xfa/cjx_object.h
index 1b7e6dc..914c4f1 100644
--- a/fxjs/xfa/cjx_object.h
+++ b/fxjs/xfa/cjx_object.h
@@ -56,9 +56,64 @@
 
 class CJX_Object {
  public:
+  // Similar, but not exactly equal to XFA_Element enum.
+  // TODO(tsepez): unify with XFA_Element.
+  enum class TypeTag {
+    Boolean,
+    Comb,
+    Container,
+    DataValue,
+    DataWindow,
+    Date,
+    DateTime,
+    Decimal,
+    Delta,
+    Desc,
+    Draw,
+    Encrypt,
+    EventPseudoModel,
+    ExclGroup,
+    ExData,
+    Extras,
+    Field,
+    Float,
+    Form,
+    Handler,
+    HostPseudoModel,
+    Image,
+    InstanceManager,
+    Integer,
+    LayoutPseudoModel,
+    List,
+    LogPseudoModel,
+    Manifest,
+    Model,
+    Node,
+    Object,
+    Occur,
+    Packet,
+    Picture,
+    Script,
+    SignaturePesudoModel,
+    Source,
+    Subform,
+    SubformSet,
+    Template,
+    Text,
+    TextNode,
+    Time,
+    Tree,
+    TreeList,
+    Value,
+    WsdlConnection,
+    Xfa,
+  };
+
   explicit CJX_Object(CXFA_Object* obj);
   virtual ~CJX_Object();
 
+  virtual bool DynamicTypeIs(TypeTag eType) const;
+
   JSE_PROP(className);
 
   CXFA_Document* GetDocument() const;
@@ -187,6 +242,7 @@
 
  private:
   using Type__ = CJX_Object;
+  static const TypeTag static_type__ = TypeTag::Object;
 
   std::pair<CXFA_Node*, int32_t> GetPropertyInternal(int32_t index,
                                                      XFA_Element eType) const;
diff --git a/fxjs/xfa/cjx_occur.cpp b/fxjs/xfa/cjx_occur.cpp
index 0b2f5ca..de8942d 100644
--- a/fxjs/xfa/cjx_occur.cpp
+++ b/fxjs/xfa/cjx_occur.cpp
@@ -15,6 +15,10 @@
 
 CJX_Occur::~CJX_Occur() = default;
 
+bool CJX_Occur::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Occur::max(CFXJSE_Value* pValue,
                     bool bSetting,
                     XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_occur.h b/fxjs/xfa/cjx_occur.h
index 87f6043..8b912b6 100644
--- a/fxjs/xfa/cjx_occur.h
+++ b/fxjs/xfa/cjx_occur.h
@@ -17,11 +17,17 @@
   explicit CJX_Occur(CXFA_Occur* node);
   ~CJX_Occur() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(max);
   JSE_PROP(min);
 
  private:
   using Type__ = CJX_Occur;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Occur;
 };
 
 #endif  // FXJS_XFA_CJX_OCCUR_H_
diff --git a/fxjs/xfa/cjx_packet.cpp b/fxjs/xfa/cjx_packet.cpp
index d91a286..dca33c2 100644
--- a/fxjs/xfa/cjx_packet.cpp
+++ b/fxjs/xfa/cjx_packet.cpp
@@ -29,6 +29,10 @@
 
 CJX_Packet::~CJX_Packet() {}
 
+bool CJX_Packet::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Packet::getAttribute(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_packet.h b/fxjs/xfa/cjx_packet.h
index 9853ac4..df4a987 100644
--- a/fxjs/xfa/cjx_packet.h
+++ b/fxjs/xfa/cjx_packet.h
@@ -17,6 +17,9 @@
   explicit CJX_Packet(CXFA_Packet* packet);
   ~CJX_Packet() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(getAttribute);
   JSE_METHOD(removeAttribute);
   JSE_METHOD(setAttribute);
@@ -25,7 +28,9 @@
 
  private:
   using Type__ = CJX_Packet;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Packet;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_picture.cpp b/fxjs/xfa/cjx_picture.cpp
index 9def3d8..d82972a 100644
--- a/fxjs/xfa/cjx_picture.cpp
+++ b/fxjs/xfa/cjx_picture.cpp
@@ -12,6 +12,10 @@
 
 CJX_Picture::~CJX_Picture() = default;
 
+bool CJX_Picture::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Picture::defaultValue(CFXJSE_Value* pValue,
                                bool bSetting,
                                XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_picture.h b/fxjs/xfa/cjx_picture.h
index 1fa3688..3e88e23 100644
--- a/fxjs/xfa/cjx_picture.h
+++ b/fxjs/xfa/cjx_picture.h
@@ -17,11 +17,17 @@
   explicit CJX_Picture(CXFA_Picture* node);
   ~CJX_Picture() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Picture;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Picture;
 };
 
 #endif  // FXJS_XFA_CJX_PICTURE_H_
diff --git a/fxjs/xfa/cjx_script.cpp b/fxjs/xfa/cjx_script.cpp
index 7c018a0..201a6df 100644
--- a/fxjs/xfa/cjx_script.cpp
+++ b/fxjs/xfa/cjx_script.cpp
@@ -13,6 +13,10 @@
 
 CJX_Script::~CJX_Script() = default;
 
+bool CJX_Script::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Script::stateless(CFXJSE_Value* pValue,
                            bool bSetting,
                            XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_script.h b/fxjs/xfa/cjx_script.h
index 337497d..cf0e1ac 100644
--- a/fxjs/xfa/cjx_script.h
+++ b/fxjs/xfa/cjx_script.h
@@ -17,12 +17,18 @@
   explicit CJX_Script(CXFA_Script* node);
   ~CJX_Script() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(stateless);
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Script;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Script;
 };
 
 #endif  // FXJS_XFA_CJX_SCRIPT_H_
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.cpp b/fxjs/xfa/cjx_signaturepseudomodel.cpp
index 1a53506..97ecd6e 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.cpp
+++ b/fxjs/xfa/cjx_signaturepseudomodel.cpp
@@ -27,6 +27,10 @@
 
 CJX_SignaturePseudoModel::~CJX_SignaturePseudoModel() {}
 
+bool CJX_SignaturePseudoModel::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_SignaturePseudoModel::verifySignature(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_signaturepseudomodel.h b/fxjs/xfa/cjx_signaturepseudomodel.h
index d830e4b..ed77238 100644
--- a/fxjs/xfa/cjx_signaturepseudomodel.h
+++ b/fxjs/xfa/cjx_signaturepseudomodel.h
@@ -17,6 +17,9 @@
   explicit CJX_SignaturePseudoModel(CScript_SignaturePseudoModel* model);
   ~CJX_SignaturePseudoModel() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(verifySignature /*verify*/);
   JSE_METHOD(sign);
   JSE_METHOD(enumerate);
@@ -24,7 +27,9 @@
 
  private:
   using Type__ = CJX_SignaturePseudoModel;
+  using ParentType__ = CJX_Object;
 
+  static const TypeTag static_type__ = TypeTag::SignaturePesudoModel;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_source.cpp b/fxjs/xfa/cjx_source.cpp
index 5279dcd..f6d3187 100644
--- a/fxjs/xfa/cjx_source.cpp
+++ b/fxjs/xfa/cjx_source.cpp
@@ -37,6 +37,10 @@
 
 CJX_Source::~CJX_Source() {}
 
+bool CJX_Source::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Source::next(CFX_V8* runtime,
                             const std::vector<v8::Local<v8::Value>>& params) {
   if (!params.empty())
diff --git a/fxjs/xfa/cjx_source.h b/fxjs/xfa/cjx_source.h
index 7948279..cd70601 100644
--- a/fxjs/xfa/cjx_source.h
+++ b/fxjs/xfa/cjx_source.h
@@ -17,6 +17,9 @@
   explicit CJX_Source(CXFA_Source* src);
   ~CJX_Source() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(addNew);
   JSE_METHOD(cancel);
   JSE_METHOD(cancelBatch);
@@ -39,7 +42,9 @@
 
  private:
   using Type__ = CJX_Source;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::Source;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_subform.cpp b/fxjs/xfa/cjx_subform.cpp
index b0c3654..ffe078f 100644
--- a/fxjs/xfa/cjx_subform.cpp
+++ b/fxjs/xfa/cjx_subform.cpp
@@ -29,6 +29,10 @@
 
 CJX_Subform::~CJX_Subform() {}
 
+bool CJX_Subform::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Subform::execEvent(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_subform.h b/fxjs/xfa/cjx_subform.h
index 8f72880..9422e82 100644
--- a/fxjs/xfa/cjx_subform.h
+++ b/fxjs/xfa/cjx_subform.h
@@ -17,6 +17,9 @@
   explicit CJX_Subform(CXFA_Node* container);
   ~CJX_Subform() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(execCalculate);
   JSE_METHOD(execEvent);
   JSE_METHOD(execInitialize);
@@ -29,7 +32,9 @@
 
  private:
   using Type__ = CJX_Subform;
+  using ParentType__ = CJX_Container;
 
+  static const TypeTag static_type__ = TypeTag::Subform;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_subformset.cpp b/fxjs/xfa/cjx_subformset.cpp
index fd9472b..0db7cd8 100644
--- a/fxjs/xfa/cjx_subformset.cpp
+++ b/fxjs/xfa/cjx_subformset.cpp
@@ -12,6 +12,10 @@
 
 CJX_SubformSet::~CJX_SubformSet() = default;
 
+bool CJX_SubformSet::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_SubformSet::instanceIndex(CFXJSE_Value* pValue,
                                    bool bSetting,
                                    XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_subformset.h b/fxjs/xfa/cjx_subformset.h
index a60b560..ce0b7d9 100644
--- a/fxjs/xfa/cjx_subformset.h
+++ b/fxjs/xfa/cjx_subformset.h
@@ -17,10 +17,16 @@
   explicit CJX_SubformSet(CXFA_SubformSet* node);
   ~CJX_SubformSet() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(instanceIndex);
 
  private:
   using Type__ = CJX_SubformSet;
+  using ParentType__ = CJX_Container;
+
+  static const TypeTag static_type__ = TypeTag::SubformSet;
 };
 
 #endif  // FXJS_XFA_CJX_SUBFORMSET_H_
diff --git a/fxjs/xfa/cjx_template.cpp b/fxjs/xfa/cjx_template.cpp
index 7890f2e..b3d9d90 100644
--- a/fxjs/xfa/cjx_template.cpp
+++ b/fxjs/xfa/cjx_template.cpp
@@ -28,6 +28,10 @@
 
 CJX_Template::~CJX_Template() {}
 
+bool CJX_Template::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Template::formNodes(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_template.h b/fxjs/xfa/cjx_template.h
index e4c819c..d396459 100644
--- a/fxjs/xfa/cjx_template.h
+++ b/fxjs/xfa/cjx_template.h
@@ -17,6 +17,9 @@
   explicit CJX_Template(CXFA_Template* tmpl);
   ~CJX_Template() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   /* The docs list a |createNode| method on Template but that method already
    * exists on Model, also the |createNode| docs say it exists on Model not
    * on Template so I'm leaving |createNode| out of the template methods. */
@@ -29,7 +32,9 @@
 
  private:
   using Type__ = CJX_Template;
+  using ParentType__ = CJX_Model;
 
+  static const TypeTag static_type__ = TypeTag::Template;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_text.cpp b/fxjs/xfa/cjx_text.cpp
index 21ef899..ddccb7e 100644
--- a/fxjs/xfa/cjx_text.cpp
+++ b/fxjs/xfa/cjx_text.cpp
@@ -12,6 +12,10 @@
 
 CJX_Text::~CJX_Text() = default;
 
+bool CJX_Text::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Text::defaultValue(CFXJSE_Value* pValue,
                             bool bSetting,
                             XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_text.h b/fxjs/xfa/cjx_text.h
index 5925f14..4109056 100644
--- a/fxjs/xfa/cjx_text.h
+++ b/fxjs/xfa/cjx_text.h
@@ -17,11 +17,17 @@
   explicit CJX_Text(CXFA_Text* node);
   ~CJX_Text() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Text;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Text;
 };
 
 #endif  // FXJS_XFA_CJX_TEXT_H_
diff --git a/fxjs/xfa/cjx_textnode.cpp b/fxjs/xfa/cjx_textnode.cpp
index 4cdcb36..756a71d 100644
--- a/fxjs/xfa/cjx_textnode.cpp
+++ b/fxjs/xfa/cjx_textnode.cpp
@@ -13,6 +13,10 @@
 
 CJX_TextNode::~CJX_TextNode() {}
 
+bool CJX_TextNode::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_TextNode::defaultValue(CFXJSE_Value* pValue,
                                 bool bSetting,
                                 XFA_Attribute attr) {
diff --git a/fxjs/xfa/cjx_textnode.h b/fxjs/xfa/cjx_textnode.h
index bf9ff14..6d74658 100644
--- a/fxjs/xfa/cjx_textnode.h
+++ b/fxjs/xfa/cjx_textnode.h
@@ -17,11 +17,17 @@
   explicit CJX_TextNode(CXFA_Node* node);
   ~CJX_TextNode() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_TextNode;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::TextNode;
 };
 
 #endif  // FXJS_XFA_CJX_TEXTNODE_H_
diff --git a/fxjs/xfa/cjx_time.cpp b/fxjs/xfa/cjx_time.cpp
index 0dc09cf..b58a25d 100644
--- a/fxjs/xfa/cjx_time.cpp
+++ b/fxjs/xfa/cjx_time.cpp
@@ -12,6 +12,10 @@
 
 CJX_Time::~CJX_Time() = default;
 
+bool CJX_Time::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Time::defaultValue(CFXJSE_Value* pValue,
                             bool bSetting,
                             XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_time.h b/fxjs/xfa/cjx_time.h
index 2b55eb4..67fa31f 100644
--- a/fxjs/xfa/cjx_time.h
+++ b/fxjs/xfa/cjx_time.h
@@ -17,11 +17,17 @@
   explicit CJX_Time(CXFA_Time* node);
   ~CJX_Time() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(defaultValue); /* {default} */
   JSE_PROP(value);
 
  private:
   using Type__ = CJX_Time;
+  using ParentType__ = CJX_Content;
+
+  static const TypeTag static_type__ = TypeTag::Time;
 };
 
 #endif  // FXJS_XFA_CJX_TIME_H_
diff --git a/fxjs/xfa/cjx_tree.cpp b/fxjs/xfa/cjx_tree.cpp
index 0eb8874..50c90cb 100644
--- a/fxjs/xfa/cjx_tree.cpp
+++ b/fxjs/xfa/cjx_tree.cpp
@@ -29,6 +29,10 @@
 
 CJX_Tree::~CJX_Tree() {}
 
+bool CJX_Tree::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_Tree::resolveNode(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_tree.h b/fxjs/xfa/cjx_tree.h
index 8b6e918..cbef888 100644
--- a/fxjs/xfa/cjx_tree.h
+++ b/fxjs/xfa/cjx_tree.h
@@ -18,6 +18,9 @@
   explicit CJX_Tree(CXFA_Object* obj);
   ~CJX_Tree() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(resolveNode);
   JSE_METHOD(resolveNodes);
 
@@ -31,13 +34,15 @@
 
  private:
   using Type__ = CJX_Tree;
+  using ParentType__ = CJX_Object;
+
+  static const TypeTag static_type__ = TypeTag::Tree;
+  static const CJX_MethodSpec MethodSpecs[];
 
   void ResolveNodeList(CFXJSE_Value* pValue,
                        WideString wsExpression,
                        uint32_t dwFlag,
                        CXFA_Node* refNode);
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_TREE_H_
diff --git a/fxjs/xfa/cjx_treelist.cpp b/fxjs/xfa/cjx_treelist.cpp
index 6b09c06..04a27e7 100644
--- a/fxjs/xfa/cjx_treelist.cpp
+++ b/fxjs/xfa/cjx_treelist.cpp
@@ -24,6 +24,10 @@
 
 CJX_TreeList::~CJX_TreeList() {}
 
+bool CJX_TreeList::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CXFA_TreeList* CJX_TreeList::GetXFATreeList() {
   return ToTreeList(GetXFAObject());
 }
diff --git a/fxjs/xfa/cjx_treelist.h b/fxjs/xfa/cjx_treelist.h
index 89551c5..294ab26 100644
--- a/fxjs/xfa/cjx_treelist.h
+++ b/fxjs/xfa/cjx_treelist.h
@@ -17,14 +17,19 @@
   explicit CJX_TreeList(CXFA_TreeList* list);
   ~CJX_TreeList() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(namedItem);
 
  private:
   using Type__ = CJX_TreeList;
+  using ParentType__ = CJX_List;
+
+  static const TypeTag static_type__ = TypeTag::TreeList;
+  static const CJX_MethodSpec MethodSpecs[];
 
   CXFA_TreeList* GetXFATreeList();
-
-  static const CJX_MethodSpec MethodSpecs[];
 };
 
 #endif  // FXJS_XFA_CJX_TREELIST_H_
diff --git a/fxjs/xfa/cjx_value.cpp b/fxjs/xfa/cjx_value.cpp
index e2ccc29..b5bf7f9 100644
--- a/fxjs/xfa/cjx_value.cpp
+++ b/fxjs/xfa/cjx_value.cpp
@@ -12,6 +12,10 @@
 
 CJX_Value::~CJX_Value() = default;
 
+bool CJX_Value::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Value::override(CFXJSE_Value* pValue,
                          bool bSetting,
                          XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_value.h b/fxjs/xfa/cjx_value.h
index b6f717f..ddc3ef2 100644
--- a/fxjs/xfa/cjx_value.h
+++ b/fxjs/xfa/cjx_value.h
@@ -17,10 +17,16 @@
   explicit CJX_Value(CXFA_Value* node);
   ~CJX_Value() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(override);
 
  private:
   using Type__ = CJX_Value;
+  using ParentType__ = CJX_Node;
+
+  static const TypeTag static_type__ = TypeTag::Value;
 };
 
 #endif  // FXJS_XFA_CJX_VALUE_H_
diff --git a/fxjs/xfa/cjx_wsdlconnection.cpp b/fxjs/xfa/cjx_wsdlconnection.cpp
index bdb4ebf..7821232 100644
--- a/fxjs/xfa/cjx_wsdlconnection.cpp
+++ b/fxjs/xfa/cjx_wsdlconnection.cpp
@@ -23,6 +23,10 @@
 
 CJX_WsdlConnection::~CJX_WsdlConnection() {}
 
+bool CJX_WsdlConnection::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 CJS_Result CJX_WsdlConnection::execute(
     CFX_V8* runtime,
     const std::vector<v8::Local<v8::Value>>& params) {
diff --git a/fxjs/xfa/cjx_wsdlconnection.h b/fxjs/xfa/cjx_wsdlconnection.h
index 12f2b7e..bd5db40 100644
--- a/fxjs/xfa/cjx_wsdlconnection.h
+++ b/fxjs/xfa/cjx_wsdlconnection.h
@@ -17,11 +17,16 @@
   explicit CJX_WsdlConnection(CXFA_WsdlConnection* connection);
   ~CJX_WsdlConnection() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_METHOD(execute);
 
  private:
   using Type__ = CJX_WsdlConnection;
+  using ParentType__ = CJX_Node;
 
+  static const TypeTag static_type__ = TypeTag::WsdlConnection;
   static const CJX_MethodSpec MethodSpecs[];
 };
 
diff --git a/fxjs/xfa/cjx_xfa.cpp b/fxjs/xfa/cjx_xfa.cpp
index e7f0d2a..7de690f 100644
--- a/fxjs/xfa/cjx_xfa.cpp
+++ b/fxjs/xfa/cjx_xfa.cpp
@@ -15,6 +15,10 @@
 
 CJX_Xfa::~CJX_Xfa() = default;
 
+bool CJX_Xfa::DynamicTypeIs(TypeTag eType) const {
+  return eType == static_type__ || ParentType__::DynamicTypeIs(eType);
+}
+
 void CJX_Xfa::thisValue(CFXJSE_Value* pValue,
                         bool bSetting,
                         XFA_Attribute eAttribute) {
diff --git a/fxjs/xfa/cjx_xfa.h b/fxjs/xfa/cjx_xfa.h
index 8f4b949..63b0f5f 100644
--- a/fxjs/xfa/cjx_xfa.h
+++ b/fxjs/xfa/cjx_xfa.h
@@ -17,10 +17,16 @@
   explicit CJX_Xfa(CXFA_Xfa* node);
   ~CJX_Xfa() override;
 
+  // CJX_Object:
+  bool DynamicTypeIs(TypeTag eType) const override;
+
   JSE_PROP(thisValue); /* this */
 
  private:
   using Type__ = CJX_Xfa;
+  using ParentType__ = CJX_Model;
+
+  static const TypeTag static_type__ = TypeTag::Xfa;
 };
 
 #endif  // FXJS_XFA_CJX_XFA_H_
diff --git a/fxjs/xfa/jse_define.h b/fxjs/xfa/jse_define.h
index 6d35df8..52bf1d1 100644
--- a/fxjs/xfa/jse_define.h
+++ b/fxjs/xfa/jse_define.h
@@ -25,7 +25,8 @@
 #define JSE_PROP(prop_name)                                               \
   static void prop_name##_static(CJX_Object* node, CFXJSE_Value* value,   \
                                  bool setting, XFA_Attribute attribute) { \
-    static_cast<Type__*>(node)->prop_name(value, setting, attribute);     \
+    if (node->DynamicTypeIs(static_type__))                               \
+      static_cast<Type__*>(node)->prop_name(value, setting, attribute);   \
   }                                                                       \
   void prop_name(CFXJSE_Value* pValue, bool bSetting, XFA_Attribute eAttribute)