Cleanup JS define methods

This CL moves DefineProps, DefineMethods and DefineConsts to the
CJS_Object and removes from the subclasses. The JSConstructor and
JSDestructor are moved to be templated static methods in JS_Defines.

Change-Id: Ibe5ee063a32ae2332b8affc843d97ee6da21f4ee
Reviewed-on: https://pdfium-review.googlesource.com/16930
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: dsinclair <dsinclair@chromium.org>
diff --git a/fpdfsdk/javascript/Document.cpp b/fpdfsdk/javascript/Document.cpp
index a5724e8..8175b0d 100644
--- a/fpdfsdk/javascript/Document.cpp
+++ b/fpdfsdk/javascript/Document.cpp
@@ -41,23 +41,12 @@
 const char* CJS_PrintParamsObj::g_pClassName = "PrintParamsObj";
 int CJS_PrintParamsObj::g_nObjDefnID = -1;
 
-void CJS_PrintParamsObj::JSConstructor(CFXJS_Engine* pEngine,
-                                       v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_PrintParamsObj(obj);
-  pObj->SetEmbedObject(new PrintParamsObj(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_PrintParamsObj::JSDestructor(CFXJS_Engine* pEngine,
-                                      v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_PrintParamsObj*>(pEngine->GetObjectPrivate(obj));
-}
-
 void CJS_PrintParamsObj::DefineJSObjects(CFXJS_Engine* pEngine,
                                          FXJSOBJTYPE eObjType) {
-  g_nObjDefnID = pEngine->DefineObj(CJS_PrintParamsObj::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
+  g_nObjDefnID =
+      pEngine->DefineObj(CJS_PrintParamsObj::g_pClassName, eObjType,
+                         JSConstructor<CJS_PrintParamsObj, PrintParamsObj>,
+                         JSDestructor<CJS_PrintParamsObj>);
 }
 
 PrintParamsObj::PrintParamsObj(CJS_Object* pJSObject)
@@ -160,40 +149,13 @@
 const char* CJS_Document::g_pClassName = "Document";
 int CJS_Document::g_nObjDefnID = -1;
 
-void CJS_Document::JSConstructor(CFXJS_Engine* pEngine,
-                                 v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Document(obj);
-  pObj->SetEmbedObject(new Document(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Document::JSDestructor(CFXJS_Engine* pEngine,
-                                v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Document*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Document::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
-void CJS_Document::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Document::DefineJSObjects(CFXJS_Engine* pEngine,
                                    FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Document::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
-  DefineMethods(pEngine);
+                                    JSConstructor<CJS_Document, Document>,
+                                    JSDestructor<CJS_Document>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 void CJS_Document::InitInstance(IJS_Runtime* pIRuntime) {
diff --git a/fpdfsdk/javascript/Document.h b/fpdfsdk/javascript/Document.h
index 6ec916c..308678f 100644
--- a/fpdfsdk/javascript/Document.h
+++ b/fpdfsdk/javascript/Document.h
@@ -41,9 +41,6 @@
   static const char* g_pClassName;
   static int g_nObjDefnID;
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
@@ -280,12 +277,7 @@
   static JSPropertySpec PropertySpecs[];
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(ADBE, ADBE, Document);
   JS_STATIC_PROP(author, author, Document);
diff --git a/fpdfsdk/javascript/Field.cpp b/fpdfsdk/javascript/Field.cpp
index 34c0c7a..288a24d 100644
--- a/fpdfsdk/javascript/Field.cpp
+++ b/fpdfsdk/javascript/Field.cpp
@@ -168,38 +168,12 @@
 const char* CJS_Field::g_pClassName = "Field";
 int CJS_Field::g_nObjDefnID = -1;
 
-void CJS_Field::JSConstructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Field(obj);
-  pObj->SetEmbedObject(new Field(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Field::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Field*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Field::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
-void CJS_Field::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Field::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Field::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
-  DefineMethods(pEngine);
+                                    JSConstructor<CJS_Field, Field>,
+                                    JSDestructor<CJS_Field>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 CJS_DelayData::CJS_DelayData(FIELD_PROP prop, int idx, const WideString& name)
diff --git a/fpdfsdk/javascript/Field.h b/fpdfsdk/javascript/Field.h
index cd43b60..5427a69 100644
--- a/fpdfsdk/javascript/Field.h
+++ b/fpdfsdk/javascript/Field.h
@@ -369,12 +369,7 @@
   static JSPropertySpec PropertySpecs[];
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(alignment, alignment, Field);
   JS_STATIC_PROP(borderStyle, border_style, Field);
diff --git a/fpdfsdk/javascript/Icon.cpp b/fpdfsdk/javascript/Icon.cpp
index d64878b..ab37e13 100644
--- a/fpdfsdk/javascript/Icon.cpp
+++ b/fpdfsdk/javascript/Icon.cpp
@@ -17,29 +17,11 @@
 const char* CJS_Icon::g_pClassName = "Icon";
 int CJS_Icon::g_nObjDefnID = -1;
 
-void CJS_Icon::JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Icon(obj);
-  pObj->SetEmbedObject(new Icon(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Icon::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Icon*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Icon::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
 void CJS_Icon::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
-  g_nObjDefnID = pEngine->DefineObj(CJS_Icon::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
+  g_nObjDefnID =
+      pEngine->DefineObj(CJS_Icon::g_pClassName, eObjType,
+                         JSConstructor<CJS_Icon, Icon>, JSDestructor<CJS_Icon>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
 }
 
 Icon::Icon(CJS_Object* pJSObject)
diff --git a/fpdfsdk/javascript/Icon.h b/fpdfsdk/javascript/Icon.h
index 8f2c8fc..2d5ad8c 100644
--- a/fpdfsdk/javascript/Icon.h
+++ b/fpdfsdk/javascript/Icon.h
@@ -35,11 +35,7 @@
   static int g_nObjDefnID;
   static JSPropertySpec PropertySpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(name, name, Icon);
 };
diff --git a/fpdfsdk/javascript/JS_Define.h b/fpdfsdk/javascript/JS_Define.h
index 7d089aa..e0535fa 100644
--- a/fpdfsdk/javascript/JS_Define.h
+++ b/fpdfsdk/javascript/JS_Define.h
@@ -21,25 +21,18 @@
 // Rich JS classes provide constants, methods, properties, and the ability
 // to construct native object state.
 
-struct JSConstSpec {
-  enum Type { Number = 0, String = 1 };
+template <class T, class A>
+static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+  CJS_Object* pObj = new T(obj);
+  pObj->SetEmbedObject(new A(pObj));
+  pEngine->SetObjectPrivate(obj, pObj);
+  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
+}
 
-  const char* pName;
-  Type eType;
-  double number;
-  const char* pStr;
-};
-
-struct JSPropertySpec {
-  const char* pName;
-  v8::AccessorGetterCallback pPropGet;
-  v8::AccessorSetterCallback pPropPut;
-};
-
-struct JSMethodSpec {
-  const char* pName;
-  v8::FunctionCallback pMethodCall;
-};
+template <class T>
+static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
+  delete static_cast<T*>(pEngine->GetObjectPrivate(obj));
+}
 
 template <class C, CJS_Return (C::*M)(CJS_Runtime*)>
 void JSPropGetter(const char* prop_name_string,
diff --git a/fpdfsdk/javascript/JS_Object.cpp b/fpdfsdk/javascript/JS_Object.cpp
index 01a4641..b6e63b5 100644
--- a/fpdfsdk/javascript/JS_Object.cpp
+++ b/fpdfsdk/javascript/JS_Object.cpp
@@ -11,7 +11,37 @@
 
 CJS_EmbedObj::CJS_EmbedObj(CJS_Object* pJSObject) : m_pJSObject(pJSObject) {}
 
-CJS_EmbedObj::~CJS_EmbedObj() {
+CJS_EmbedObj::~CJS_EmbedObj() {}
+
+// static
+void CJS_Object::DefineConsts(CFXJS_Engine* pEngine,
+                              int objId,
+                              const JSConstSpec consts[]) {
+  for (size_t i = 0; consts[i].pName != 0; ++i) {
+    pEngine->DefineObjConst(
+        objId, consts[i].pName,
+        consts[i].eType == JSConstSpec::Number
+            ? pEngine->NewNumber(consts[i].number).As<v8::Value>()
+            : pEngine->NewString(consts[i].pStr).As<v8::Value>());
+  }
+}
+
+// static
+void CJS_Object::DefineProps(CFXJS_Engine* pEngine,
+                             int objId,
+                             const JSPropertySpec props[]) {
+  for (size_t i = 0; props[i].pName != 0; ++i) {
+    pEngine->DefineObjProperty(objId, props[i].pName, props[i].pPropGet,
+                               props[i].pPropPut);
+  }
+}
+
+// static
+void CJS_Object::DefineMethods(CFXJS_Engine* pEngine,
+                               int objId,
+                               const JSMethodSpec methods[]) {
+  for (size_t i = 0; methods[i].pName != 0; ++i)
+    pEngine->DefineObjMethod(objId, methods[i].pName, methods[i].pMethodCall);
 }
 
 CJS_Object::CJS_Object(v8::Local<v8::Object> pObject) {
diff --git a/fpdfsdk/javascript/JS_Object.h b/fpdfsdk/javascript/JS_Object.h
index e0a96a9..20d5203 100644
--- a/fpdfsdk/javascript/JS_Object.h
+++ b/fpdfsdk/javascript/JS_Object.h
@@ -18,6 +18,26 @@
 class CJS_Object;
 class CPDFSDK_FormFillEnvironment;
 
+struct JSConstSpec {
+  enum Type { Number = 0, String = 1 };
+
+  const char* pName;
+  Type eType;
+  double number;
+  const char* pStr;
+};
+
+struct JSPropertySpec {
+  const char* pName;
+  v8::AccessorGetterCallback pPropGet;
+  v8::AccessorSetterCallback pPropPut;
+};
+
+struct JSMethodSpec {
+  const char* pName;
+  v8::FunctionCallback pMethodCall;
+};
+
 class CJS_EmbedObj {
  public:
   explicit CJS_EmbedObj(CJS_Object* pJSObject);
@@ -31,6 +51,16 @@
 
 class CJS_Object {
  public:
+  static void DefineConsts(CFXJS_Engine* pEngine,
+                           int objId,
+                           const JSConstSpec consts[]);
+  static void DefineProps(CFXJS_Engine* pEngine,
+                          int objId,
+                          const JSPropertySpec props[]);
+  static void DefineMethods(CFXJS_Engine* pEngine,
+                            int objId,
+                            const JSMethodSpec methods[]);
+
   explicit CJS_Object(v8::Local<v8::Object> pObject);
   virtual ~CJS_Object();
 
diff --git a/fpdfsdk/javascript/app.cpp b/fpdfsdk/javascript/app.cpp
index 84bca4a..0b331a8 100644
--- a/fpdfsdk/javascript/app.cpp
+++ b/fpdfsdk/javascript/app.cpp
@@ -145,23 +145,11 @@
 const char* CJS_TimerObj::g_pClassName = "TimerObj";
 int CJS_TimerObj::g_nObjDefnID = -1;
 
-void CJS_TimerObj::JSConstructor(CFXJS_Engine* pEngine,
-                                 v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_TimerObj(obj);
-  pObj->SetEmbedObject(new TimerObj(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_TimerObj::JSDestructor(CFXJS_Engine* pEngine,
-                                v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_TimerObj*>(pEngine->GetObjectPrivate(obj));
-}
-
 void CJS_TimerObj::DefineJSObjects(CFXJS_Engine* pEngine,
                                    FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_TimerObj::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
+                                    JSConstructor<CJS_TimerObj, TimerObj>,
+                                    JSDestructor<CJS_TimerObj>);
 }
 
 TimerObj::TimerObj(CJS_Object* pJSObject)
@@ -226,37 +214,12 @@
 const char* CJS_App::g_pClassName = "app";
 int CJS_App::g_nObjDefnID = -1;
 
-void CJS_App::JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_App(obj);
-  pObj->SetEmbedObject(new app(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_App::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_App*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_App::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
-void CJS_App::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_App::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
-  g_nObjDefnID = pEngine->DefineObj(CJS_App::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
-  DefineMethods(pEngine);
+  g_nObjDefnID =
+      pEngine->DefineObj(CJS_App::g_pClassName, eObjType,
+                         JSConstructor<CJS_App, app>, JSDestructor<CJS_App>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 app::app(CJS_Object* pJSObject)
diff --git a/fpdfsdk/javascript/app.h b/fpdfsdk/javascript/app.h
index 36d2a84..454bb42 100644
--- a/fpdfsdk/javascript/app.h
+++ b/fpdfsdk/javascript/app.h
@@ -36,9 +36,6 @@
   static const char* g_pClassName;
   static int g_nObjDefnID;
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
@@ -154,12 +151,7 @@
   static JSPropertySpec PropertySpecs[];
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(activeDocs, active_docs, app);
   JS_STATIC_PROP(calculate, calculate, app);
diff --git a/fpdfsdk/javascript/cjs_annot.cpp b/fpdfsdk/javascript/cjs_annot.cpp
index 8847642..9f528a3 100644
--- a/fpdfsdk/javascript/cjs_annot.cpp
+++ b/fpdfsdk/javascript/cjs_annot.cpp
@@ -28,30 +28,11 @@
 const char* CJS_Annot::g_pClassName = "Annot";
 int CJS_Annot::g_nObjDefnID = -1;
 
-void CJS_Annot::JSConstructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Annot(obj);
-  pObj->SetEmbedObject(new Annot(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Annot::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Annot*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Annot::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
 void CJS_Annot::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Annot::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
+                                    JSConstructor<CJS_Annot, Annot>,
+                                    JSDestructor<CJS_Annot>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
 }
 
 Annot::Annot(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
diff --git a/fpdfsdk/javascript/cjs_annot.h b/fpdfsdk/javascript/cjs_annot.h
index 086392c..a16e510 100644
--- a/fpdfsdk/javascript/cjs_annot.h
+++ b/fpdfsdk/javascript/cjs_annot.h
@@ -39,11 +39,7 @@
   static int g_nObjDefnID;
   static JSPropertySpec PropertySpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(hidden, hidden, Annot);
   JS_STATIC_PROP(name, name, Annot);
diff --git a/fpdfsdk/javascript/cjs_border.cpp b/fpdfsdk/javascript/cjs_border.cpp
index 50e69b2..6bb2e09 100644
--- a/fpdfsdk/javascript/cjs_border.cpp
+++ b/fpdfsdk/javascript/cjs_border.cpp
@@ -17,18 +17,8 @@
 const char* CJS_Border::g_pClassName = "border";
 int CJS_Border::g_nObjDefnID = -1;
 
-void CJS_Border::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Border::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID =
       pEngine->DefineObj(CJS_Border::g_pClassName, eObjType, nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_border.h b/fpdfsdk/javascript/cjs_border.h
index c3ce247..26a710a 100644
--- a/fpdfsdk/javascript/cjs_border.h
+++ b/fpdfsdk/javascript/cjs_border.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_BORDER_H_
diff --git a/fpdfsdk/javascript/cjs_display.cpp b/fpdfsdk/javascript/cjs_display.cpp
index df05ed2..5d604b5 100644
--- a/fpdfsdk/javascript/cjs_display.cpp
+++ b/fpdfsdk/javascript/cjs_display.cpp
@@ -15,18 +15,8 @@
 const char* CJS_Display::g_pClassName = "display";
 int CJS_Display::g_nObjDefnID = -1;
 
-void CJS_Display::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Display::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID =
       pEngine->DefineObj(CJS_Display::g_pClassName, eObjType, nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_display.h b/fpdfsdk/javascript/cjs_display.h
index f92eb86..61e741d 100644
--- a/fpdfsdk/javascript/cjs_display.h
+++ b/fpdfsdk/javascript/cjs_display.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_DISPLAY_H_
diff --git a/fpdfsdk/javascript/cjs_font.cpp b/fpdfsdk/javascript/cjs_font.cpp
index 799702c..0a9f02f 100644
--- a/fpdfsdk/javascript/cjs_font.cpp
+++ b/fpdfsdk/javascript/cjs_font.cpp
@@ -26,18 +26,8 @@
 const char* CJS_Font::g_pClassName = "font";
 int CJS_Font::g_nObjDefnID = -1;
 
-void CJS_Font::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Font::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID =
       pEngine->DefineObj(CJS_Font::g_pClassName, eObjType, nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_font.h b/fpdfsdk/javascript/cjs_font.h
index d45a578..7881cdf 100644
--- a/fpdfsdk/javascript/cjs_font.h
+++ b/fpdfsdk/javascript/cjs_font.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_FONT_H_
diff --git a/fpdfsdk/javascript/cjs_global.cpp b/fpdfsdk/javascript/cjs_global.cpp
index d81fe04..1ec9bbf 100644
--- a/fpdfsdk/javascript/cjs_global.cpp
+++ b/fpdfsdk/javascript/cjs_global.cpp
@@ -199,44 +199,6 @@
   pGlobal->Initial(pRuntime->GetFormFillEnv());
 }
 
-void CJS_Global::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
-void CJS_Global::JSConstructor(CFXJS_Engine* pEngine,
-                               v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Global(obj);
-  pObj->SetEmbedObject(new JSGlobalAlternate(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Global::JSDestructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Global*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Global::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
-void CJS_Global::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Global::queryprop_static(
     v8::Local<v8::String> property,
     const v8::PropertyCallbackInfo<v8::Integer>& info) {
@@ -269,11 +231,12 @@
 }
 
 void CJS_Global::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
-  g_nObjDefnID = pEngine->DefineObj(CJS_Global::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineConsts(pEngine);
-  DefineProps(pEngine);
-  DefineMethods(pEngine);
+  g_nObjDefnID = pEngine->DefineObj(
+      CJS_Global::g_pClassName, eObjType,
+      JSConstructor<CJS_Global, JSGlobalAlternate>, JSDestructor<CJS_Global>);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
   DefineAllProperties(pEngine);
 }
 
diff --git a/fpdfsdk/javascript/cjs_global.h b/fpdfsdk/javascript/cjs_global.h
index 96788f7..8174418 100644
--- a/fpdfsdk/javascript/cjs_global.h
+++ b/fpdfsdk/javascript/cjs_global.h
@@ -19,18 +19,13 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
-
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void DefineProps(CFXJS_Engine* pEngine);
-  static void DefineMethods(CFXJS_Engine* pEngine);
   static JSPropertySpec PropertySpecs[];
   static JSMethodSpec MethodSpecs[];
 
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
+  static void DefineAllProperties(CFXJS_Engine* pEngine);
+
   static void queryprop_static(
       v8::Local<v8::String> property,
       const v8::PropertyCallbackInfo<v8::Integer>& info);
@@ -41,7 +36,6 @@
                              const v8::PropertyCallbackInfo<v8::Value>& info);
   static void delprop_static(v8::Local<v8::String> property,
                              const v8::PropertyCallbackInfo<v8::Boolean>& info);
-  static void DefineAllProperties(CFXJS_Engine* pEngine);
 
   static void setPersistent_static(
       const v8::FunctionCallbackInfo<v8::Value>& info);
diff --git a/fpdfsdk/javascript/cjs_highlight.cpp b/fpdfsdk/javascript/cjs_highlight.cpp
index 6cd40dc..5a5f7bf 100644
--- a/fpdfsdk/javascript/cjs_highlight.cpp
+++ b/fpdfsdk/javascript/cjs_highlight.cpp
@@ -16,19 +16,9 @@
 const char* CJS_Highlight::g_pClassName = "highlight";
 int CJS_Highlight::g_nObjDefnID = -1;
 
-void CJS_Highlight::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Highlight::DefineJSObjects(CFXJS_Engine* pEngine,
                                     FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Highlight::g_pClassName, eObjType,
                                     nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_highlight.h b/fpdfsdk/javascript/cjs_highlight.h
index 0ac645d..dd16e0c 100644
--- a/fpdfsdk/javascript/cjs_highlight.h
+++ b/fpdfsdk/javascript/cjs_highlight.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_HIGHLIGHT_H_
diff --git a/fpdfsdk/javascript/cjs_position.cpp b/fpdfsdk/javascript/cjs_position.cpp
index 5c33546..46b908b 100644
--- a/fpdfsdk/javascript/cjs_position.cpp
+++ b/fpdfsdk/javascript/cjs_position.cpp
@@ -19,19 +19,9 @@
 const char* CJS_Position::g_pClassName = "position";
 int CJS_Position::g_nObjDefnID = -1;
 
-void CJS_Position::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Position::DefineJSObjects(CFXJS_Engine* pEngine,
                                    FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Position::g_pClassName, eObjType,
                                     nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_position.h b/fpdfsdk/javascript/cjs_position.h
index 54d12c5..f180af4 100644
--- a/fpdfsdk/javascript/cjs_position.h
+++ b/fpdfsdk/javascript/cjs_position.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_POSITION_H_
diff --git a/fpdfsdk/javascript/cjs_scalehow.cpp b/fpdfsdk/javascript/cjs_scalehow.cpp
index 25160ba..d593d83 100644
--- a/fpdfsdk/javascript/cjs_scalehow.cpp
+++ b/fpdfsdk/javascript/cjs_scalehow.cpp
@@ -14,19 +14,9 @@
 const char* CJS_ScaleHow::g_pClassName = "scaleHow";
 int CJS_ScaleHow::g_nObjDefnID = -1;
 
-void CJS_ScaleHow::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_ScaleHow::DefineJSObjects(CFXJS_Engine* pEngine,
                                    FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_ScaleHow::g_pClassName, eObjType,
                                     nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_scalehow.h b/fpdfsdk/javascript/cjs_scalehow.h
index c742398..2d3906e 100644
--- a/fpdfsdk/javascript/cjs_scalehow.h
+++ b/fpdfsdk/javascript/cjs_scalehow.h
@@ -16,10 +16,10 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
+  static JSConstSpec ConstSpecs[];
+
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 
-  static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_SCALEHOW_H_
diff --git a/fpdfsdk/javascript/cjs_scalewhen.cpp b/fpdfsdk/javascript/cjs_scalewhen.cpp
index 22c6784..129a150 100644
--- a/fpdfsdk/javascript/cjs_scalewhen.cpp
+++ b/fpdfsdk/javascript/cjs_scalewhen.cpp
@@ -16,19 +16,9 @@
 const char* CJS_ScaleWhen::g_pClassName = "scaleWhen";
 int CJS_ScaleWhen::g_nObjDefnID = -1;
 
-void CJS_ScaleWhen::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_ScaleWhen::DefineJSObjects(CFXJS_Engine* pEngine,
                                     FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_ScaleWhen::g_pClassName, eObjType,
                                     nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_scalewhen.h b/fpdfsdk/javascript/cjs_scalewhen.h
index d7727e9..72e8de1 100644
--- a/fpdfsdk/javascript/cjs_scalewhen.h
+++ b/fpdfsdk/javascript/cjs_scalewhen.h
@@ -16,10 +16,10 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
+  static JSConstSpec ConstSpecs[];
+
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 
-  static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_SCALEWHEN_H_
diff --git a/fpdfsdk/javascript/cjs_style.cpp b/fpdfsdk/javascript/cjs_style.cpp
index 872851f..5706413 100644
--- a/fpdfsdk/javascript/cjs_style.cpp
+++ b/fpdfsdk/javascript/cjs_style.cpp
@@ -18,18 +18,8 @@
 const char* CJS_Style::g_pClassName = "style";
 int CJS_Style::g_nObjDefnID = -1;
 
-void CJS_Style::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Style::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID =
       pEngine->DefineObj(CJS_Style::g_pClassName, eObjType, nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_style.h b/fpdfsdk/javascript/cjs_style.h
index 0c98f50..2609f3e 100644
--- a/fpdfsdk/javascript/cjs_style.h
+++ b/fpdfsdk/javascript/cjs_style.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_STYLE_H_
diff --git a/fpdfsdk/javascript/cjs_zoomtype.cpp b/fpdfsdk/javascript/cjs_zoomtype.cpp
index fb5e2ab..8870776 100644
--- a/fpdfsdk/javascript/cjs_zoomtype.cpp
+++ b/fpdfsdk/javascript/cjs_zoomtype.cpp
@@ -19,19 +19,9 @@
 const char* CJS_Zoomtype::g_pClassName = "zoomtype";
 int CJS_Zoomtype::g_nObjDefnID = -1;
 
-void CJS_Zoomtype::DefineConsts(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(ConstSpecs) - 1; ++i) {
-    pEngine->DefineObjConst(
-        g_nObjDefnID, ConstSpecs[i].pName,
-        ConstSpecs[i].eType == JSConstSpec::Number
-            ? pEngine->NewNumber(ConstSpecs[i].number).As<v8::Value>()
-            : pEngine->NewString(ConstSpecs[i].pStr).As<v8::Value>());
-  }
-}
-
 void CJS_Zoomtype::DefineJSObjects(CFXJS_Engine* pEngine,
                                    FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Zoomtype::g_pClassName, eObjType,
                                     nullptr, nullptr);
-  DefineConsts(pEngine);
+  DefineConsts(pEngine, g_nObjDefnID, ConstSpecs);
 }
diff --git a/fpdfsdk/javascript/cjs_zoomtype.h b/fpdfsdk/javascript/cjs_zoomtype.h
index b204d58..e7dd7da 100644
--- a/fpdfsdk/javascript/cjs_zoomtype.h
+++ b/fpdfsdk/javascript/cjs_zoomtype.h
@@ -16,10 +16,9 @@
 
   static const char* g_pClassName;
   static int g_nObjDefnID;
-  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-
   static JSConstSpec ConstSpecs[];
-  static void DefineConsts(CFXJS_Engine* pEngine);
+
+  static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_CJS_ZOOMTYPE_H_
diff --git a/fpdfsdk/javascript/color.cpp b/fpdfsdk/javascript/color.cpp
index 25008b4..0e05ddf 100644
--- a/fpdfsdk/javascript/color.cpp
+++ b/fpdfsdk/javascript/color.cpp
@@ -37,38 +37,12 @@
 const char* CJS_Color::g_pClassName = "color";
 int CJS_Color::g_nObjDefnID = -1;
 
-void CJS_Color::JSConstructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Color(obj);
-  pObj->SetEmbedObject(new color(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Color::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Color*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Color::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
-void CJS_Color::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Color::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Color::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
-  DefineMethods(pEngine);
+                                    JSConstructor<CJS_Color, color>,
+                                    JSDestructor<CJS_Color>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 // static
diff --git a/fpdfsdk/javascript/color.h b/fpdfsdk/javascript/color.h
index dd5ca41..5705787 100644
--- a/fpdfsdk/javascript/color.h
+++ b/fpdfsdk/javascript/color.h
@@ -93,12 +93,7 @@
   static JSPropertySpec PropertySpecs[];
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(black, black, color);
   JS_STATIC_PROP(blue, blue, color);
diff --git a/fpdfsdk/javascript/console.cpp b/fpdfsdk/javascript/console.cpp
index eb566b0..dbe8e32 100644
--- a/fpdfsdk/javascript/console.cpp
+++ b/fpdfsdk/javascript/console.cpp
@@ -23,30 +23,11 @@
 const char* CJS_Console::g_pClassName = "console";
 int CJS_Console::g_nObjDefnID = -1;
 
-void CJS_Console::JSConstructor(CFXJS_Engine* pEngine,
-                                v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Console(obj);
-  pObj->SetEmbedObject(new console(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Console::JSDestructor(CFXJS_Engine* pEngine,
-                               v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Console*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Console::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Console::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Console::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineMethods(pEngine);
+                                    JSConstructor<CJS_Console, console>,
+                                    JSDestructor<CJS_Console>);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 console::console(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
diff --git a/fpdfsdk/javascript/console.h b/fpdfsdk/javascript/console.h
index 1d85765..ca0c144 100644
--- a/fpdfsdk/javascript/console.h
+++ b/fpdfsdk/javascript/console.h
@@ -36,11 +36,7 @@
   static int g_nObjDefnID;
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_METHOD(clear, console);
   JS_STATIC_METHOD(hide, console);
diff --git a/fpdfsdk/javascript/event.cpp b/fpdfsdk/javascript/event.cpp
index f20ac40..61aa546 100644
--- a/fpdfsdk/javascript/event.cpp
+++ b/fpdfsdk/javascript/event.cpp
@@ -39,30 +39,11 @@
 const char* CJS_Event::g_pClassName = "event";
 int CJS_Event::g_nObjDefnID = -1;
 
-void CJS_Event::JSConstructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Event(obj);
-  pObj->SetEmbedObject(new event(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Event::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Event*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Event::DefineProps(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) {
-    pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName,
-                               PropertySpecs[i].pPropGet,
-                               PropertySpecs[i].pPropPut);
-  }
-}
-
 void CJS_Event::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Event::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineProps(pEngine);
+                                    JSConstructor<CJS_Event, event>,
+                                    JSDestructor<CJS_Event>);
+  DefineProps(pEngine, g_nObjDefnID, PropertySpecs);
 }
 
 event::event(CJS_Object* pJsObject) : CJS_EmbedObj(pJsObject) {}
diff --git a/fpdfsdk/javascript/event.h b/fpdfsdk/javascript/event.h
index 4d03f45..6974563 100644
--- a/fpdfsdk/javascript/event.h
+++ b/fpdfsdk/javascript/event.h
@@ -84,11 +84,7 @@
   static int g_nObjDefnID;
   static JSPropertySpec PropertySpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineProps(CFXJS_Engine* pEngine);
 
   JS_STATIC_PROP(change, change, event);
   JS_STATIC_PROP(changeEx, change_ex, event);
diff --git a/fpdfsdk/javascript/report.cpp b/fpdfsdk/javascript/report.cpp
index 7652ce4..eec6118 100644
--- a/fpdfsdk/javascript/report.cpp
+++ b/fpdfsdk/javascript/report.cpp
@@ -19,30 +19,11 @@
 const char* CJS_Report::g_pClassName = "Report";
 int CJS_Report::g_nObjDefnID = -1;
 
-void CJS_Report::JSConstructor(CFXJS_Engine* pEngine,
-                               v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Report(obj);
-  pObj->SetEmbedObject(new Report(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Report::JSDestructor(CFXJS_Engine* pEngine,
-                              v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Report*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Report::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Report::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
   g_nObjDefnID = pEngine->DefineObj(CJS_Report::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineMethods(pEngine);
+                                    JSConstructor<CJS_Report, Report>,
+                                    JSDestructor<CJS_Report>);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 Report::Report(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
diff --git a/fpdfsdk/javascript/report.h b/fpdfsdk/javascript/report.h
index d6fe4e7..13a6718 100644
--- a/fpdfsdk/javascript/report.h
+++ b/fpdfsdk/javascript/report.h
@@ -32,11 +32,7 @@
   static int g_nObjDefnID;
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_METHOD(save, Report)
   JS_STATIC_METHOD(writeText, Report);
diff --git a/fpdfsdk/javascript/util.cpp b/fpdfsdk/javascript/util.cpp
index 08846c1..75b0db3 100644
--- a/fpdfsdk/javascript/util.cpp
+++ b/fpdfsdk/javascript/util.cpp
@@ -36,28 +36,11 @@
 const char* CJS_Util::g_pClassName = "util";
 int CJS_Util::g_nObjDefnID = -1;
 
-void CJS_Util::JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  CJS_Object* pObj = new CJS_Util(obj);
-  pObj->SetEmbedObject(new util(pObj));
-  pEngine->SetObjectPrivate(obj, pObj);
-  pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));
-}
-
-void CJS_Util::JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj) {
-  delete static_cast<CJS_Util*>(pEngine->GetObjectPrivate(obj));
-}
-
-void CJS_Util::DefineMethods(CFXJS_Engine* pEngine) {
-  for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) {
-    pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName,
-                             MethodSpecs[i].pMethodCall);
-  }
-}
-
 void CJS_Util::DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType) {
-  g_nObjDefnID = pEngine->DefineObj(CJS_Util::g_pClassName, eObjType,
-                                    JSConstructor, JSDestructor);
-  DefineMethods(pEngine);
+  g_nObjDefnID =
+      pEngine->DefineObj(CJS_Util::g_pClassName, eObjType,
+                         JSConstructor<CJS_Util, util>, JSDestructor<CJS_Util>);
+  DefineMethods(pEngine, g_nObjDefnID, MethodSpecs);
 }
 
 namespace {
diff --git a/fpdfsdk/javascript/util.h b/fpdfsdk/javascript/util.h
index 8903eb9..c3ede9a 100644
--- a/fpdfsdk/javascript/util.h
+++ b/fpdfsdk/javascript/util.h
@@ -51,11 +51,7 @@
   static int g_nObjDefnID;
   static JSMethodSpec MethodSpecs[];
 
-  static void JSConstructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-  static void JSDestructor(CFXJS_Engine* pEngine, v8::Local<v8::Object> obj);
-
   static void DefineJSObjects(CFXJS_Engine* pEngine, FXJSOBJTYPE eObjType);
-  static void DefineMethods(CFXJS_Engine* pEngine);
 
   JS_STATIC_METHOD(printd, util);
   JS_STATIC_METHOD(printf, util);