Cleaning up JS macros

This Cl moves JS_Defines macros with only one usage to be inline. Moves
macros to where they're used and expands as needed.

Change-Id: I7296aa0b7a815ef8f2a80dd813e7466056fe37af
Reviewed-on: https://pdfium-review.googlesource.com/16510
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: dsinclair <dsinclair@chromium.org>
diff --git a/fpdfsdk/javascript/Annot.cpp b/fpdfsdk/javascript/Annot.cpp
index 867cfc4..19e94f3 100644
--- a/fpdfsdk/javascript/Annot.cpp
+++ b/fpdfsdk/javascript/Annot.cpp
@@ -29,7 +29,7 @@
 
 JSMethodSpec CJS_Annot::MethodSpecs[] = {{0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Annot, Annot)
+IMPLEMENT_JS_CLASS(CJS_Annot, Annot, Annot)
 
 Annot::Annot(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
 
diff --git a/fpdfsdk/javascript/Annot.h b/fpdfsdk/javascript/Annot.h
index 5e06ae8..00872a4 100644
--- a/fpdfsdk/javascript/Annot.h
+++ b/fpdfsdk/javascript/Annot.h
@@ -7,8 +7,6 @@
 #ifndef FPDFSDK_JAVASCRIPT_ANNOT_H_
 #define FPDFSDK_JAVASCRIPT_ANNOT_H_
 
-#include <memory>
-
 #include "fpdfsdk/cpdfsdk_baannot.h"
 #include "fpdfsdk/javascript/JS_Define.h"
 
diff --git a/fpdfsdk/javascript/Consts.cpp b/fpdfsdk/javascript/Consts.cpp
index 8c01ae0..4dfd5c8 100644
--- a/fpdfsdk/javascript/Consts.cpp
+++ b/fpdfsdk/javascript/Consts.cpp
@@ -10,6 +10,16 @@
 #include "fpdfsdk/javascript/JS_Object.h"
 #include "fpdfsdk/javascript/JS_Value.h"
 
+#define IMPLEMENT_JS_CLASS_CONST(js_class_name, class_name)                  \
+  IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                    \
+  IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                   \
+  void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                 \
+                                      FXJSOBJTYPE eObjType) {                \
+    g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType, \
+                                      nullptr, nullptr);                     \
+    DefineConsts(pEngine);                                                   \
+  }
+
 JSConstSpec CJS_Border::ConstSpecs[] = {
     {"s", JSConstSpec::String, 0, "solid"},
     {"b", JSConstSpec::String, 0, "beveled"},
diff --git a/fpdfsdk/javascript/Consts.h b/fpdfsdk/javascript/Consts.h
index e358cb9..3b292c2 100644
--- a/fpdfsdk/javascript/Consts.h
+++ b/fpdfsdk/javascript/Consts.h
@@ -14,7 +14,8 @@
   explicit CJS_Border(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Border() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Display : public CJS_Object {
@@ -22,7 +23,8 @@
   explicit CJS_Display(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Display() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Font : public CJS_Object {
@@ -30,7 +32,8 @@
   explicit CJS_Font(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Font() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Highlight : public CJS_Object {
@@ -38,7 +41,8 @@
   explicit CJS_Highlight(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Highlight() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Position : public CJS_Object {
@@ -46,7 +50,8 @@
   explicit CJS_Position(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Position() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_ScaleHow : public CJS_Object {
@@ -54,7 +59,8 @@
   explicit CJS_ScaleHow(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_ScaleHow() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_ScaleWhen : public CJS_Object {
@@ -62,7 +68,8 @@
   explicit CJS_ScaleWhen(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_ScaleWhen() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Style : public CJS_Object {
@@ -70,7 +77,8 @@
   explicit CJS_Style(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Style() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_Zoomtype : public CJS_Object {
@@ -78,7 +86,8 @@
   explicit CJS_Zoomtype(v8::Local<v8::Object> pObject) : CJS_Object(pObject) {}
   ~CJS_Zoomtype() override {}
 
-  DECLARE_JS_CLASS_CONST();
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
 };
 
 class CJS_GlobalConsts : public CJS_Object {
diff --git a/fpdfsdk/javascript/Document.cpp b/fpdfsdk/javascript/Document.cpp
index 30375ba..1f023fb 100644
--- a/fpdfsdk/javascript/Document.cpp
+++ b/fpdfsdk/javascript/Document.cpp
@@ -44,7 +44,7 @@
 
 JSMethodSpec CJS_PrintParamsObj::MethodSpecs[] = {{0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_PrintParamsObj, PrintParamsObj)
+IMPLEMENT_JS_CLASS(CJS_PrintParamsObj, PrintParamsObj, PrintParamsObj)
 
 PrintParamsObj::PrintParamsObj(CJS_Object* pJSObject)
     : CJS_EmbedObj(pJSObject) {
@@ -145,7 +145,7 @@
     {"mailDoc", mailDoc_static},
     {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Document, Document)
+IMPLEMENT_JS_CLASS(CJS_Document, Document, Document)
 
 void CJS_Document::InitInstance(IJS_Runtime* pIRuntime) {
   CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
diff --git a/fpdfsdk/javascript/Field.cpp b/fpdfsdk/javascript/Field.cpp
index f315301..b6c32b5 100644
--- a/fpdfsdk/javascript/Field.cpp
+++ b/fpdfsdk/javascript/Field.cpp
@@ -167,7 +167,7 @@
     {"signatureValidate", signatureValidate_static},
     {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Field, Field)
+IMPLEMENT_JS_CLASS(CJS_Field, Field, Field)
 
 CJS_DelayData::CJS_DelayData(FIELD_PROP prop, int idx, const WideString& name)
     : eProp(prop), nControlIndex(idx), sFieldName(name) {}
diff --git a/fpdfsdk/javascript/Icon.cpp b/fpdfsdk/javascript/Icon.cpp
index 7c340af..a5fa3fa 100644
--- a/fpdfsdk/javascript/Icon.cpp
+++ b/fpdfsdk/javascript/Icon.cpp
@@ -18,7 +18,7 @@
 
 JSMethodSpec CJS_Icon::MethodSpecs[] = {{0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Icon, Icon)
+IMPLEMENT_JS_CLASS(CJS_Icon, Icon, Icon)
 
 Icon::Icon(CJS_Object* pJSObject)
     : CJS_EmbedObj(pJSObject), m_swIconName(L"") {}
diff --git a/fpdfsdk/javascript/JS_Define.h b/fpdfsdk/javascript/JS_Define.h
index e6a9303..4bcdcdf 100644
--- a/fpdfsdk/javascript/JS_Define.h
+++ b/fpdfsdk/javascript/JS_Define.h
@@ -139,13 +139,6 @@
                                                    info);                     \
   }
 
-#define JS_SPECIAL_STATIC_METHOD(method_name, class_alternate, class_name) \
-  static void method_name##_static(                                        \
-      const v8::FunctionCallbackInfo<v8::Value>& info) {                   \
-    JSMethod<class_alternate, &class_alternate::method_name>(              \
-        #method_name, #class_name, info);                                  \
-  }
-
 // All JS classes have a name, an object defintion ID, and the ability to
 // register themselves with FXJS_V8. We never make a BASE class on its own
 // because it can't really do anything.
@@ -158,21 +151,6 @@
   const char* js_class_name::g_pClassName = #class_name;        \
   int js_class_name::g_nObjDefnID = -1;
 
-// CONST classes provide constants, but not constructors, methods, or props.
-#define DECLARE_JS_CLASS_CONST() \
-  DECLARE_JS_CLASS_BASE_PART()   \
-  DECLARE_JS_CLASS_CONST_PART()
-
-#define IMPLEMENT_JS_CLASS_CONST(js_class_name, class_name)                  \
-  IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                    \
-  IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                   \
-  void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                 \
-                                      FXJSOBJTYPE eObjType) {                \
-    g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType, \
-                                      nullptr, nullptr);                     \
-    DefineConsts(pEngine);                                                   \
-  }
-
 #define DECLARE_JS_CLASS_CONST_PART() \
   static JSConstSpec ConstSpecs[];    \
   static void DefineConsts(CFXJS_Engine* pEngine);
@@ -188,22 +166,17 @@
     }                                                                    \
   }
 
-// Convenience macros for declaring classes without an alternate.
-#define DECLARE_JS_CLASS() DECLARE_JS_CLASS_RICH()
-#define IMPLEMENT_JS_CLASS(js_class_name, class_name) \
-  IMPLEMENT_JS_CLASS_RICH(js_class_name, class_name, class_name)
-
 // Rich JS classes provide constants, methods, properties, and the ability
 // to construct native object state.
-#define DECLARE_JS_CLASS_RICH() \
+#define DECLARE_JS_CLASS()      \
   DECLARE_JS_CLASS_BASE_PART()  \
   DECLARE_JS_CLASS_CONST_PART() \
-  DECLARE_JS_CLASS_RICH_PART()
+  DECLARE_JS_CLASS_PART()
 
-#define IMPLEMENT_JS_CLASS_RICH(js_class_name, class_alternate, class_name)  \
+#define IMPLEMENT_JS_CLASS(js_class_name, class_alternate, class_name)       \
   IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                    \
   IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                   \
-  IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, class_name)   \
+  IMPLEMENT_JS_CLASS_PART(js_class_name, class_alternate, class_name)        \
   void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                 \
                                       FXJSOBJTYPE eObjType) {                \
     g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType, \
@@ -213,7 +186,7 @@
     DefineMethods(pEngine);                                                  \
   }
 
-#define DECLARE_JS_CLASS_RICH_PART()                                           \
+#define DECLARE_JS_CLASS_PART()                                                \
   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);                              \
@@ -221,235 +194,30 @@
   static JSPropertySpec PropertySpecs[];                                       \
   static JSMethodSpec MethodSpecs[];
 
-#define IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate,    \
-                                     class_name)                        \
-  void js_class_name::JSConstructor(CFXJS_Engine* pEngine,              \
-                                    v8::Local<v8::Object> obj) {        \
-    CJS_Object* pObj = new js_class_name(obj);                          \
-    pObj->SetEmbedObject(new class_alternate(pObj));                    \
-    pEngine->SetObjectPrivate(obj, pObj);                               \
-    pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));             \
-  }                                                                     \
-  void js_class_name::JSDestructor(CFXJS_Engine* pEngine,               \
-                                   v8::Local<v8::Object> obj) {         \
-    delete static_cast<js_class_name*>(pEngine->GetObjectPrivate(obj)); \
-  }                                                                     \
-  void js_class_name::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 js_class_name::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);             \
-    }                                                                   \
-  }
-
-// Special JS classes implement methods, props, and queries, but not consts.
-#define DECLARE_SPECIAL_JS_CLASS() \
-  DECLARE_JS_CLASS_BASE_PART()     \
-  DECLARE_JS_CLASS_CONST_PART()    \
-  DECLARE_JS_CLASS_RICH_PART()     \
-  DECLARE_SPECIAL_JS_CLASS_PART()
-
-#define IMPLEMENT_SPECIAL_JS_CLASS(js_class_name, class_alternate, class_name) \
-  IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                      \
-  IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                     \
-  IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, class_name)     \
-  IMPLEMENT_SPECIAL_JS_CLASS_PART(js_class_name, class_alternate, class_name)  \
-  void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                   \
-                                      FXJSOBJTYPE eObjType) {                  \
-    g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType,   \
-                                      JSConstructor, JSDestructor);            \
-    DefineConsts(pEngine);                                                     \
-    DefineProps(pEngine);                                                      \
-    DefineMethods(pEngine);                                                    \
-    DefineAllProperties(pEngine);                                              \
-  }
-
-#define DECLARE_SPECIAL_JS_CLASS_PART()                                        \
-  static void queryprop_static(                                                \
-      v8::Local<v8::String> property,                                          \
-      const v8::PropertyCallbackInfo<v8::Integer>& info);                      \
-  static void getprop_static(v8::Local<v8::String> property,                   \
-                             const v8::PropertyCallbackInfo<v8::Value>& info); \
-  static void putprop_static(v8::Local<v8::String> property,                   \
-                             v8::Local<v8::Value> value,                       \
-                             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);
-
-#define IMPLEMENT_SPECIAL_JS_CLASS_PART(js_class_name, class_alternate,    \
-                                        class_name)                        \
-  void js_class_name::queryprop_static(                                    \
-      v8::Local<v8::String> property,                                      \
-      const v8::PropertyCallbackInfo<v8::Integer>& info) {                 \
-    JSSpecialPropQuery<class_alternate>(#class_name, property, info);      \
-  }                                                                        \
-  void js_class_name::getprop_static(                                      \
-      v8::Local<v8::String> property,                                      \
-      const v8::PropertyCallbackInfo<v8::Value>& info) {                   \
-    JSSpecialPropGet<class_alternate>(#class_name, property, info);        \
-  }                                                                        \
-  void js_class_name::putprop_static(                                      \
-      v8::Local<v8::String> property, v8::Local<v8::Value> value,          \
-      const v8::PropertyCallbackInfo<v8::Value>& info) {                   \
-    JSSpecialPropPut<class_alternate>(#class_name, property, value, info); \
-  }                                                                        \
-  void js_class_name::delprop_static(                                      \
-      v8::Local<v8::String> property,                                      \
-      const v8::PropertyCallbackInfo<v8::Boolean>& info) {                 \
-    JSSpecialPropDel<class_alternate>(#class_name, property, info);        \
-  }                                                                        \
-  void js_class_name::DefineAllProperties(CFXJS_Engine* pEngine) {         \
-    pEngine->DefineObjAllProperties(                                       \
-        g_nObjDefnID, js_class_name::queryprop_static,                     \
-        js_class_name::getprop_static, js_class_name::putprop_static,      \
-        js_class_name::delprop_static);                                    \
-  }
-
-template <class Alt>
-void JSSpecialPropQuery(const char*,
-                        v8::Local<v8::String> property,
-                        const v8::PropertyCallbackInfo<v8::Integer>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  v8::String::Utf8Value utf8_value(property);
-  WideString propname =
-      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-  bool bRet = pObj->QueryProperty(propname.c_str());
-  info.GetReturnValue().Set(bRet ? 4 : 0);
-}
-
-template <class Alt>
-void JSSpecialPropGet(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  v8::String::Utf8Value utf8_value(property);
-  WideString propname =
-      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-
-  CJS_Value value(pRuntime);
-  if (!pObj->GetProperty(pRuntime, propname.c_str(), &value)) {
-    pRuntime->Error(JSFormatErrorString(class_name, "GetProperty", L""));
-    return;
-  }
-  info.GetReturnValue().Set(value.ToV8Value(pRuntime));
-}
-
-template <class Alt>
-void JSSpecialPropPut(const char* class_name,
-                      v8::Local<v8::String> property,
-                      v8::Local<v8::Value> value,
-                      const v8::PropertyCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  v8::String::Utf8Value utf8_value(property);
-  WideString propname =
-      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-  CJS_Value prop_value(pRuntime, value);
-  if (!pObj->SetProperty(pRuntime, propname.c_str(), prop_value)) {
-    pRuntime->Error(JSFormatErrorString(class_name, "PutProperty", L""));
-  }
-}
-
-template <class Alt>
-void JSSpecialPropDel(const char* class_name,
-                      v8::Local<v8::String> property,
-                      const v8::PropertyCallbackInfo<v8::Boolean>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-
-  CJS_Object* pJSObj =
-      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
-  if (!pJSObj)
-    return;
-
-  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
-  v8::String::Utf8Value utf8_value(property);
-  WideString propname =
-      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
-  if (!pObj->DelProperty(pRuntime, propname.c_str())) {
-    ByteString cbName;
-    cbName.Format("%s.%s", class_name, "DelProperty");
-    // Probably a missing call to JSFX_Error().
-  }
-}
-
-template <bool (
-    *F)(CJS_Runtime*, const std::vector<CJS_Value>&, CJS_Value&, WideString&)>
-void JSGlobalFunc(const char* func_name_string,
-                  const v8::FunctionCallbackInfo<v8::Value>& info) {
-  CJS_Runtime* pRuntime =
-      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
-  if (!pRuntime)
-    return;
-  std::vector<CJS_Value> parameters;
-  for (unsigned int i = 0; i < (unsigned int)info.Length(); i++) {
-    parameters.push_back(CJS_Value(pRuntime, info[i]));
-  }
-  CJS_Value valueRes(pRuntime);
-  WideString sError;
-  if (!(*F)(pRuntime, parameters, valueRes, sError)) {
-    pRuntime->Error(JSFormatErrorString(func_name_string, nullptr, sError));
-    return;
-  }
-  info.GetReturnValue().Set(valueRes.ToV8Value(pRuntime));
-}
-
-#define JS_STATIC_GLOBAL_FUN(fun_name)                   \
-  static void fun_name##_static(                         \
-      const v8::FunctionCallbackInfo<v8::Value>& info) { \
-    JSGlobalFunc<fun_name>(#fun_name, info);             \
-  }
-
-#define JS_STATIC_DECLARE_GLOBAL_FUN()       \
-  static JSMethodSpec GlobalFunctionSpecs[]; \
-  static void DefineJSObjects(CFXJS_Engine* pEngine)
-
-#define IMPLEMENT_JS_STATIC_GLOBAL_FUN(js_class_name)                    \
-  void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine) {           \
-    for (size_t i = 0; i < FX_ArraySize(GlobalFunctionSpecs) - 1; ++i) { \
-      pEngine->DefineGlobalMethod(                                       \
-          js_class_name::GlobalFunctionSpecs[i].pName,                   \
-          js_class_name::GlobalFunctionSpecs[i].pMethodCall);            \
-    }                                                                    \
+#define IMPLEMENT_JS_CLASS_PART(js_class_name, class_alternate, class_name) \
+  void js_class_name::JSConstructor(CFXJS_Engine* pEngine,                  \
+                                    v8::Local<v8::Object> obj) {            \
+    CJS_Object* pObj = new js_class_name(obj);                              \
+    pObj->SetEmbedObject(new class_alternate(pObj));                        \
+    pEngine->SetObjectPrivate(obj, pObj);                                   \
+    pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine));                 \
+  }                                                                         \
+  void js_class_name::JSDestructor(CFXJS_Engine* pEngine,                   \
+                                   v8::Local<v8::Object> obj) {             \
+    delete static_cast<js_class_name*>(pEngine->GetObjectPrivate(obj));     \
+  }                                                                         \
+  void js_class_name::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 js_class_name::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);                 \
+    }                                                                       \
   }
 
 #endif  // FPDFSDK_JAVASCRIPT_JS_DEFINE_H_
diff --git a/fpdfsdk/javascript/PublicMethods.cpp b/fpdfsdk/javascript/PublicMethods.cpp
index a48f647..2e7387e 100644
--- a/fpdfsdk/javascript/PublicMethods.cpp
+++ b/fpdfsdk/javascript/PublicMethods.cpp
@@ -57,8 +57,6 @@
     {"AFExtractNums", AFExtractNums_static},
     {0, 0}};
 
-IMPLEMENT_JS_STATIC_GLOBAL_FUN(CJS_PublicMethods)
-
 namespace {
 
 const wchar_t* const months[] = {L"Jan", L"Feb", L"Mar", L"Apr",
@@ -113,8 +111,68 @@
 }
 #endif
 
+// NOLINTNEXTLINE(whitespace/parens)
+template <bool (
+    *F)(CJS_Runtime*, const std::vector<CJS_Value>&, CJS_Value&, WideString&)>
+void JSGlobalFunc(const char* func_name_string,
+                  const v8::FunctionCallbackInfo<v8::Value>& info) {
+  CJS_Runtime* pRuntime =
+      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  if (!pRuntime)
+    return;
+  std::vector<CJS_Value> parameters;
+  for (unsigned int i = 0; i < (unsigned int)info.Length(); i++) {
+    parameters.push_back(CJS_Value(pRuntime, info[i]));
+  }
+  CJS_Value valueRes(pRuntime);
+  WideString sError;
+  if (!(*F)(pRuntime, parameters, valueRes, sError)) {
+    pRuntime->Error(JSFormatErrorString(func_name_string, nullptr, sError));
+    return;
+  }
+  info.GetReturnValue().Set(valueRes.ToV8Value(pRuntime));
+}
+
 }  // namespace
 
+// static
+void CJS_PublicMethods::DefineJSObjects(CFXJS_Engine* pEngine) {
+  for (size_t i = 0; i < FX_ArraySize(GlobalFunctionSpecs) - 1; ++i) {
+    pEngine->DefineGlobalMethod(
+        CJS_PublicMethods::GlobalFunctionSpecs[i].pName,
+        CJS_PublicMethods::GlobalFunctionSpecs[i].pMethodCall);
+  }
+}
+
+#define JS_STATIC_GLOBAL_FUN(fun_name)                   \
+  void CJS_PublicMethods::fun_name##_static(             \
+      const v8::FunctionCallbackInfo<v8::Value>& info) { \
+    JSGlobalFunc<fun_name>(#fun_name, info);             \
+  }
+
+JS_STATIC_GLOBAL_FUN(AFNumber_Format);
+JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke);
+JS_STATIC_GLOBAL_FUN(AFPercent_Format);
+JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke);
+JS_STATIC_GLOBAL_FUN(AFDate_FormatEx);
+JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx);
+JS_STATIC_GLOBAL_FUN(AFDate_Format);
+JS_STATIC_GLOBAL_FUN(AFDate_Keystroke);
+JS_STATIC_GLOBAL_FUN(AFTime_FormatEx);
+JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx);
+JS_STATIC_GLOBAL_FUN(AFTime_Format);
+JS_STATIC_GLOBAL_FUN(AFTime_Keystroke);
+JS_STATIC_GLOBAL_FUN(AFSpecial_Format);
+JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke);
+JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx);
+JS_STATIC_GLOBAL_FUN(AFSimple);
+JS_STATIC_GLOBAL_FUN(AFMakeNumber);
+JS_STATIC_GLOBAL_FUN(AFSimple_Calculate);
+JS_STATIC_GLOBAL_FUN(AFRange_Validate);
+JS_STATIC_GLOBAL_FUN(AFMergeChange);
+JS_STATIC_GLOBAL_FUN(AFParseDateEx);
+JS_STATIC_GLOBAL_FUN(AFExtractNums);
+
 bool CJS_PublicMethods::IsNumber(const WideString& str) {
   WideString sTrim = StrTrim(str);
   const wchar_t* pTrim = sTrim.c_str();
diff --git a/fpdfsdk/javascript/PublicMethods.h b/fpdfsdk/javascript/PublicMethods.h
index abce08b..1d30832 100644
--- a/fpdfsdk/javascript/PublicMethods.h
+++ b/fpdfsdk/javascript/PublicMethods.h
@@ -107,31 +107,52 @@
                             CJS_Value& vRet,
                             WideString& sError);
 
-  JS_STATIC_GLOBAL_FUN(AFNumber_Format);
-  JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke);
-  JS_STATIC_GLOBAL_FUN(AFPercent_Format);
-  JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke);
-  JS_STATIC_GLOBAL_FUN(AFDate_FormatEx);
-  JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx);
-  JS_STATIC_GLOBAL_FUN(AFDate_Format);
-  JS_STATIC_GLOBAL_FUN(AFDate_Keystroke);
-  JS_STATIC_GLOBAL_FUN(AFTime_FormatEx);
-  JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx);
-  JS_STATIC_GLOBAL_FUN(AFTime_Format);
-  JS_STATIC_GLOBAL_FUN(AFTime_Keystroke);
-  JS_STATIC_GLOBAL_FUN(AFSpecial_Format);
-  JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke);
-  JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx);
-  JS_STATIC_GLOBAL_FUN(AFSimple);
-  JS_STATIC_GLOBAL_FUN(AFMakeNumber);
-  JS_STATIC_GLOBAL_FUN(AFSimple_Calculate);
-  JS_STATIC_GLOBAL_FUN(AFRange_Validate);
-  JS_STATIC_GLOBAL_FUN(AFMergeChange);
-  JS_STATIC_GLOBAL_FUN(AFParseDateEx);
-  JS_STATIC_GLOBAL_FUN(AFExtractNums);
+  static void AFNumber_Format_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFNumber_Keystroke_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFPercent_Format_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFPercent_Keystroke_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFDate_FormatEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFDate_KeystrokeEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFDate_Format_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFDate_Keystroke_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFTime_FormatEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFTime_KeystrokeEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFTime_Format_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFTime_Keystroke_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFSpecial_Format_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFSpecial_Keystroke_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFSpecial_KeystrokeEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFSimple_static(const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFMakeNumber_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFSimple_Calculate_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFRange_Validate_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFMergeChange_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFParseDateEx_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void AFExtractNums_static(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
 
-  JS_STATIC_DECLARE_GLOBAL_FUN();
-
+  static JSMethodSpec GlobalFunctionSpecs[];
+  static void DefineJSObjects(CFXJS_Engine* pEngine);
   static int ParseStringInteger(const WideString& string,
                                 size_t nStart,
                                 size_t& nSkip,
diff --git a/fpdfsdk/javascript/app.cpp b/fpdfsdk/javascript/app.cpp
index 0567f5a5..9894bb8 100644
--- a/fpdfsdk/javascript/app.cpp
+++ b/fpdfsdk/javascript/app.cpp
@@ -137,7 +137,7 @@
 
 JSMethodSpec CJS_TimerObj::MethodSpecs[] = {{0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_TimerObj, TimerObj)
+IMPLEMENT_JS_CLASS(CJS_TimerObj, TimerObj, TimerObj)
 
 TimerObj::TimerObj(CJS_Object* pJSObject)
     : CJS_EmbedObj(pJSObject), m_nTimerID(0) {}
@@ -200,7 +200,7 @@
                                        {"setTimeOut", setTimeOut_static},
                                        {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_App, app)
+IMPLEMENT_JS_CLASS(CJS_App, app, app)
 
 app::app(CJS_Object* pJSObject)
     : CJS_EmbedObj(pJSObject), m_bCalculate(true), m_bRuntimeHighLight(false) {}
diff --git a/fpdfsdk/javascript/color.cpp b/fpdfsdk/javascript/color.cpp
index 5d8282d..09a6a09 100644
--- a/fpdfsdk/javascript/color.cpp
+++ b/fpdfsdk/javascript/color.cpp
@@ -36,7 +36,7 @@
                                          {"equal", equal_static},
                                          {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Color, color)
+IMPLEMENT_JS_CLASS(CJS_Color, color, color)
 
 color::color(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {
   m_crTransparent = CFX_Color(CFX_Color::kTransparent);
diff --git a/fpdfsdk/javascript/console.cpp b/fpdfsdk/javascript/console.cpp
index 4f0fd43..5e854bd 100644
--- a/fpdfsdk/javascript/console.cpp
+++ b/fpdfsdk/javascript/console.cpp
@@ -24,7 +24,7 @@
                                            {"show", show_static},
                                            {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Console, console)
+IMPLEMENT_JS_CLASS(CJS_Console, console, console)
 
 console::console(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
 
diff --git a/fpdfsdk/javascript/event.cpp b/fpdfsdk/javascript/event.cpp
index 34504d5..11b8b01 100644
--- a/fpdfsdk/javascript/event.cpp
+++ b/fpdfsdk/javascript/event.cpp
@@ -40,7 +40,7 @@
 
 JSMethodSpec CJS_Event::MethodSpecs[] = {{0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Event, event)
+IMPLEMENT_JS_CLASS(CJS_Event, event, event)
 
 event::event(CJS_Object* pJsObject) : CJS_EmbedObj(pJsObject) {}
 
diff --git a/fpdfsdk/javascript/global.cpp b/fpdfsdk/javascript/global.cpp
index 7c82e4d..ee76469 100644
--- a/fpdfsdk/javascript/global.cpp
+++ b/fpdfsdk/javascript/global.cpp
@@ -6,6 +6,8 @@
 
 #include "fpdfsdk/javascript/global.h"
 
+#include <map>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -13,11 +15,209 @@
 #include "fpdfsdk/javascript/JS_Define.h"
 #include "fpdfsdk/javascript/JS_EventHandler.h"
 #include "fpdfsdk/javascript/JS_GlobalData.h"
+#include "fpdfsdk/javascript/JS_KeyValue.h"
 #include "fpdfsdk/javascript/JS_Object.h"
 #include "fpdfsdk/javascript/JS_Value.h"
 #include "fpdfsdk/javascript/cjs_event_context.h"
 #include "fpdfsdk/javascript/resource.h"
 
+#define IMPLEMENT_SPECIAL_JS_CLASS(js_class_name, class_alternate, class_name) \
+  IMPLEMENT_JS_CLASS_BASE_PART(js_class_name, class_name)                      \
+  IMPLEMENT_JS_CLASS_CONST_PART(js_class_name, class_name)                     \
+  IMPLEMENT_JS_CLASS_PART(js_class_name, class_alternate, class_name)          \
+  void js_class_name::queryprop_static(                                        \
+      v8::Local<v8::String> property,                                          \
+      const v8::PropertyCallbackInfo<v8::Integer>& info) {                     \
+    JSSpecialPropQuery<class_alternate>(#class_name, property, info);          \
+  }                                                                            \
+  void js_class_name::getprop_static(                                          \
+      v8::Local<v8::String> property,                                          \
+      const v8::PropertyCallbackInfo<v8::Value>& info) {                       \
+    JSSpecialPropGet<class_alternate>(#class_name, property, info);            \
+  }                                                                            \
+  void js_class_name::putprop_static(                                          \
+      v8::Local<v8::String> property, v8::Local<v8::Value> value,              \
+      const v8::PropertyCallbackInfo<v8::Value>& info) {                       \
+    JSSpecialPropPut<class_alternate>(#class_name, property, value, info);     \
+  }                                                                            \
+  void js_class_name::delprop_static(                                          \
+      v8::Local<v8::String> property,                                          \
+      const v8::PropertyCallbackInfo<v8::Boolean>& info) {                     \
+    JSSpecialPropDel<class_alternate>(#class_name, property, info);            \
+  }                                                                            \
+  void js_class_name::DefineAllProperties(CFXJS_Engine* pEngine) {             \
+    pEngine->DefineObjAllProperties(                                           \
+        g_nObjDefnID, js_class_name::queryprop_static,                         \
+        js_class_name::getprop_static, js_class_name::putprop_static,          \
+        js_class_name::delprop_static);                                        \
+  }                                                                            \
+  void js_class_name::DefineJSObjects(CFXJS_Engine* pEngine,                   \
+                                      FXJSOBJTYPE eObjType) {                  \
+    g_nObjDefnID = pEngine->DefineObj(js_class_name::g_pClassName, eObjType,   \
+                                      JSConstructor, JSDestructor);            \
+    DefineConsts(pEngine);                                                     \
+    DefineProps(pEngine);                                                      \
+    DefineMethods(pEngine);                                                    \
+    DefineAllProperties(pEngine);                                              \
+  }
+
+namespace {
+
+template <class Alt>
+void JSSpecialPropQuery(const char*,
+                        v8::Local<v8::String> property,
+                        const v8::PropertyCallbackInfo<v8::Integer>& info) {
+  CJS_Runtime* pRuntime =
+      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  if (!pRuntime)
+    return;
+
+  CJS_Object* pJSObj =
+      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+  if (!pJSObj)
+    return;
+
+  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
+  v8::String::Utf8Value utf8_value(property);
+  WideString propname =
+      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
+  bool bRet = pObj->QueryProperty(propname.c_str());
+  info.GetReturnValue().Set(bRet ? 4 : 0);
+}
+
+template <class Alt>
+void JSSpecialPropGet(const char* class_name,
+                      v8::Local<v8::String> property,
+                      const v8::PropertyCallbackInfo<v8::Value>& info) {
+  CJS_Runtime* pRuntime =
+      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  if (!pRuntime)
+    return;
+
+  CJS_Object* pJSObj =
+      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+  if (!pJSObj)
+    return;
+
+  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
+  v8::String::Utf8Value utf8_value(property);
+  WideString propname =
+      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
+
+  CJS_Value value(pRuntime);
+  if (!pObj->GetProperty(pRuntime, propname.c_str(), &value)) {
+    pRuntime->Error(JSFormatErrorString(class_name, "GetProperty", L""));
+    return;
+  }
+  info.GetReturnValue().Set(value.ToV8Value(pRuntime));
+}
+
+template <class Alt>
+void JSSpecialPropPut(const char* class_name,
+                      v8::Local<v8::String> property,
+                      v8::Local<v8::Value> value,
+                      const v8::PropertyCallbackInfo<v8::Value>& info) {
+  CJS_Runtime* pRuntime =
+      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  if (!pRuntime)
+    return;
+
+  CJS_Object* pJSObj =
+      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+  if (!pJSObj)
+    return;
+
+  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
+  v8::String::Utf8Value utf8_value(property);
+  WideString propname =
+      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
+  CJS_Value prop_value(pRuntime, value);
+  if (!pObj->SetProperty(pRuntime, propname.c_str(), prop_value)) {
+    pRuntime->Error(JSFormatErrorString(class_name, "PutProperty", L""));
+  }
+}
+
+template <class Alt>
+void JSSpecialPropDel(const char* class_name,
+                      v8::Local<v8::String> property,
+                      const v8::PropertyCallbackInfo<v8::Boolean>& info) {
+  CJS_Runtime* pRuntime =
+      CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
+  if (!pRuntime)
+    return;
+
+  CJS_Object* pJSObj =
+      static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+  if (!pJSObj)
+    return;
+
+  Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
+  v8::String::Utf8Value utf8_value(property);
+  WideString propname =
+      WideString::FromUTF8(ByteStringView(*utf8_value, utf8_value.length()));
+  if (!pObj->DelProperty(pRuntime, propname.c_str())) {
+    ByteString cbName;
+    cbName.Format("%s.%s", class_name, "DelProperty");
+    // Probably a missing call to JSFX_Error().
+  }
+}
+
+struct JSGlobalData {
+  JSGlobalData();
+  ~JSGlobalData();
+
+  JS_GlobalDataType nType;
+  double dData;
+  bool bData;
+  ByteString sData;
+  v8::Global<v8::Object> pData;
+  bool bPersistent;
+  bool bDeleted;
+};
+
+class JSGlobalAlternate : public CJS_EmbedObj {
+ public:
+  explicit JSGlobalAlternate(CJS_Object* pJSObject);
+  ~JSGlobalAlternate() override;
+
+  bool setPersistent(CJS_Runtime* pRuntime,
+                     const std::vector<CJS_Value>& params,
+                     CJS_Value& vRet,
+                     WideString& sError);
+  bool QueryProperty(const wchar_t* propname);
+  bool GetProperty(CJS_Runtime* pRuntime,
+                   const wchar_t* propname,
+                   CJS_Value* vp);
+  bool SetProperty(CJS_Runtime* pRuntime,
+                   const wchar_t* propname,
+                   const CJS_Value& vp);
+  bool DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
+  void Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv);
+
+ private:
+  void UpdateGlobalPersistentVariables();
+  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
+  void DestroyGlobalPersisitentVariables();
+  bool SetGlobalVariables(const ByteString& propname,
+                          JS_GlobalDataType nType,
+                          double dData,
+                          bool bData,
+                          const ByteString& sData,
+                          v8::Local<v8::Object> pData,
+                          bool bDefaultPersistent);
+  void ObjectToArray(CJS_Runtime* pRuntime,
+                     v8::Local<v8::Object> pObj,
+                     CJS_GlobalVariableArray& array);
+  void PutObjectProperty(v8::Local<v8::Object> obj, CJS_KeyValue* pData);
+
+  std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
+  WideString m_sFilePath;
+  CJS_GlobalData* m_pGlobalData;
+  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
+};
+
+}  // namespace
+
 JSConstSpec CJS_Global::ConstSpecs[] = {{0, JSConstSpec::Number, 0, 0}};
 
 JSPropertySpec CJS_Global::PropertySpecs[] = {{0, 0, 0}};
@@ -28,6 +228,13 @@
 
 IMPLEMENT_SPECIAL_JS_CLASS(CJS_Global, JSGlobalAlternate, global);
 
+// static
+void CJS_Global::setPersistent_static(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  JSMethod<JSGlobalAlternate, &JSGlobalAlternate::setPersistent>(
+      "setPersistent", "global", info);
+}
+
 void CJS_Global::InitInstance(IJS_Runtime* pIRuntime) {
   CJS_Runtime* pRuntime = static_cast<CJS_Runtime*>(pIRuntime);
   JSGlobalAlternate* pGlobal =
diff --git a/fpdfsdk/javascript/global.h b/fpdfsdk/javascript/global.h
index e9c6f67..77794a2 100644
--- a/fpdfsdk/javascript/global.h
+++ b/fpdfsdk/javascript/global.h
@@ -7,70 +7,7 @@
 #ifndef FPDFSDK_JAVASCRIPT_GLOBAL_H_
 #define FPDFSDK_JAVASCRIPT_GLOBAL_H_
 
-#include <map>
-#include <memory>
-#include <vector>
-
 #include "fpdfsdk/javascript/JS_Define.h"
-#include "fpdfsdk/javascript/JS_KeyValue.h"
-
-class CJS_GlobalData;
-class CJS_GlobalVariableArray;
-class CJS_KeyValue;
-
-struct JSGlobalData {
-  JSGlobalData();
-  ~JSGlobalData();
-
-  JS_GlobalDataType nType;
-  double dData;
-  bool bData;
-  ByteString sData;
-  v8::Global<v8::Object> pData;
-  bool bPersistent;
-  bool bDeleted;
-};
-
-class JSGlobalAlternate : public CJS_EmbedObj {
- public:
-  explicit JSGlobalAlternate(CJS_Object* pJSObject);
-  ~JSGlobalAlternate() override;
-
-  bool setPersistent(CJS_Runtime* pRuntime,
-                     const std::vector<CJS_Value>& params,
-                     CJS_Value& vRet,
-                     WideString& sError);
-  bool QueryProperty(const wchar_t* propname);
-  bool GetProperty(CJS_Runtime* pRuntime,
-                   const wchar_t* propname,
-                   CJS_Value* vp);
-  bool SetProperty(CJS_Runtime* pRuntime,
-                   const wchar_t* propname,
-                   const CJS_Value& vp);
-  bool DelProperty(CJS_Runtime* pRuntime, const wchar_t* propname);
-  void Initial(CPDFSDK_FormFillEnvironment* pFormFillEnv);
-
- private:
-  void UpdateGlobalPersistentVariables();
-  void CommitGlobalPersisitentVariables(CJS_Runtime* pRuntime);
-  void DestroyGlobalPersisitentVariables();
-  bool SetGlobalVariables(const ByteString& propname,
-                          JS_GlobalDataType nType,
-                          double dData,
-                          bool bData,
-                          const ByteString& sData,
-                          v8::Local<v8::Object> pData,
-                          bool bDefaultPersistent);
-  void ObjectToArray(CJS_Runtime* pRuntime,
-                     v8::Local<v8::Object> pObj,
-                     CJS_GlobalVariableArray& array);
-  void PutObjectProperty(v8::Local<v8::Object> obj, CJS_KeyValue* pData);
-
-  std::map<ByteString, std::unique_ptr<JSGlobalData>> m_MapGlobal;
-  WideString m_sFilePath;
-  CJS_GlobalData* m_pGlobalData;
-  CPDFSDK_FormFillEnvironment::ObservedPtr m_pFormFillEnv;
-};
 
 class CJS_Global : public CJS_Object {
  public:
@@ -80,8 +17,23 @@
   // CJS_Object
   void InitInstance(IJS_Runtime* pIRuntime) override;
 
-  DECLARE_SPECIAL_JS_CLASS();
-  JS_SPECIAL_STATIC_METHOD(setPersistent, JSGlobalAlternate, global);
+  DECLARE_JS_CLASS_BASE_PART();
+  DECLARE_JS_CLASS_CONST_PART();
+  DECLARE_JS_CLASS_PART();
+  static void queryprop_static(
+      v8::Local<v8::String> property,
+      const v8::PropertyCallbackInfo<v8::Integer>& info);
+  static void getprop_static(v8::Local<v8::String> property,
+                             const v8::PropertyCallbackInfo<v8::Value>& info);
+  static void putprop_static(v8::Local<v8::String> property,
+                             v8::Local<v8::Value> value,
+                             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);
 };
 
 #endif  // FPDFSDK_JAVASCRIPT_GLOBAL_H_
diff --git a/fpdfsdk/javascript/report.cpp b/fpdfsdk/javascript/report.cpp
index 4550943..8f5d9ac 100644
--- a/fpdfsdk/javascript/report.cpp
+++ b/fpdfsdk/javascript/report.cpp
@@ -20,7 +20,7 @@
                                           {"writeText", writeText_static},
                                           {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Report, Report)
+IMPLEMENT_JS_CLASS(CJS_Report, Report, Report)
 
 Report::Report(CJS_Object* pJSObject) : CJS_EmbedObj(pJSObject) {}
 
diff --git a/fpdfsdk/javascript/util.cpp b/fpdfsdk/javascript/util.cpp
index 109c0ec..9b63823 100644
--- a/fpdfsdk/javascript/util.cpp
+++ b/fpdfsdk/javascript/util.cpp
@@ -37,7 +37,7 @@
     {"printx", printx_static},         {"scand", scand_static},
     {"byteToChar", byteToChar_static}, {0, 0}};
 
-IMPLEMENT_JS_CLASS(CJS_Util, util)
+IMPLEMENT_JS_CLASS(CJS_Util, util, util)
 
 namespace {