Use v8::Global<> directly in CFXJSE_FormCalcContext and tests.

This is complicated by the need to have additional handle scopes
to permit the use of v8::Local<> in several places.

Bug: pdfium:1610
Change-Id: I60eebcc1eecd397efb1bd60bf2d62f1aa91fb869
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/75950
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
diff --git a/fxjs/fxv8.cpp b/fxjs/fxv8.cpp
index 614a122..0f2c656 100644
--- a/fxjs/fxv8.cpp
+++ b/fxjs/fxv8.cpp
@@ -113,6 +113,11 @@
   return pValue->BooleanValue(pIsolate);
 }
 
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue) {
+  return static_cast<float>(ReentrantToDoubleHelper(pIsolate, pValue));
+}
+
 double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
                                v8::Local<v8::Value> pValue) {
   if (pValue.IsEmpty())
diff --git a/fxjs/fxv8.h b/fxjs/fxv8.h
index b3c2649..d5c19fa 100644
--- a/fxjs/fxv8.h
+++ b/fxjs/fxv8.h
@@ -42,9 +42,12 @@
 v8::Local<v8::Object> NewObjectHelper(v8::Isolate* pIsolate);
 v8::Local<v8::Date> NewDateHelper(v8::Isolate* pIsolate, double d);
 
-int ReentrantToInt32Helper(v8::Isolate* pIsolate, v8::Local<v8::Value> pValue);
+int32_t ReentrantToInt32Helper(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> pValue);
 bool ReentrantToBooleanHelper(v8::Isolate* pIsolate,
                               v8::Local<v8::Value> pValue);
+float ReentrantToFloatHelper(v8::Isolate* pIsolate,
+                             v8::Local<v8::Value> pValue);
 double ReentrantToDoubleHelper(v8::Isolate* pIsolate,
                                v8::Local<v8::Value> pValue);
 WideString ReentrantToWideStringHelper(v8::Isolate* pIsolate,
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index e2f30d7..0771d2d 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -5766,11 +5766,13 @@
                                                CFXJSE_Context* pScriptContext,
                                                CXFA_Document* pDoc)
     : m_pIsolate(pIsolate),
-      m_pValue(std::make_unique<CFXJSE_Value>()),
       m_pDocument(pDoc) {
-  m_pValue->SetHostObject(
-      pIsolate, this,
-      CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false));
+  m_Value.Reset(
+      m_pIsolate,
+      NewBoundV8Object(
+          m_pIsolate,
+          CFXJSE_Class::Create(pScriptContext, &kFormCalcFM2JSDescriptor, false)
+              ->GetTemplate(m_pIsolate)));
 }
 
 CFXJSE_FormCalcContext::~CFXJSE_FormCalcContext() = default;
@@ -5780,7 +5782,8 @@
 }
 
 void CFXJSE_FormCalcContext::GlobalPropertyGetter(CFXJSE_Value* pValue) {
-  pValue->Assign(GetIsolate(), m_pValue.get());
+  pValue->ForceSetValue(GetIsolate(),
+                        v8::Local<v8::Value>::New(m_pIsolate, m_Value));
 }
 
 // static
diff --git a/fxjs/xfa/cfxjse_formcalc_context.h b/fxjs/xfa/cfxjse_formcalc_context.h
index 19b88ca..83f7790 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.h
+++ b/fxjs/xfa/cfxjse_formcalc_context.h
@@ -329,8 +329,8 @@
   void ThrowParamCountMismatchException(const WideString& method) const;
   void ThrowException(const WideString& str) const;
 
-  UnownedPtr<v8::Isolate> m_pIsolate;
-  std::unique_ptr<CFXJSE_Value> m_pValue;
+  UnownedPtr<v8::Isolate> const m_pIsolate;
+  v8::Global<v8::Value> m_Value;
   UnownedPtr<CXFA_Document> const m_pDocument;
 };
 
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index ddd0eda..ad523a6 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -4,6 +4,7 @@
 
 #include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/xfa_js_embedder_test.h"
@@ -23,34 +24,40 @@
   void ExecuteExpectNull(ByteStringView input) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNull(isolate())) << "Program: " << input;
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    EXPECT_TRUE(fxv8::IsNull(GetValue())) << "Program: " << input;
   }
 
   void ExecuteExpectBool(ByteStringView input, bool expected) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+
     // Yes, bools might be integers, somehow.
-    EXPECT_TRUE(value->IsBoolean(isolate()) || value->IsInteger(isolate()))
+    EXPECT_TRUE(fxv8::IsBoolean(value) || fxv8::IsInteger(value))
         << "Program: " << input;
-    EXPECT_EQ(expected, value->ToBoolean(isolate())) << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToBooleanHelper(isolate(), value))
+        << "Program: " << input;
   }
 
   void ExecuteExpectInt32(ByteStringView input, int32_t expected) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsInteger(isolate())) << "Program: " << input;
-    EXPECT_EQ(expected, value->ToInteger(isolate())) << "Program: " << input;
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsInteger(value)) << "Program: " << input;
+    EXPECT_EQ(expected, fxv8::ReentrantToInt32Helper(isolate(), value))
+        << "Program: " << input;
   }
 
   void ExecuteExpectFloat(ByteStringView input, float expected) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber(isolate())) << "Program: " << input;
-    EXPECT_FLOAT_EQ(expected, value->ToFloat(isolate()))
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_FLOAT_EQ(expected, fxv8::ReentrantToFloatHelper(isolate(), value))
         << "Program: " << input;
   }
 
@@ -59,18 +66,22 @@
                               float precision) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsNumber(isolate())) << "Program: " << input;
-    EXPECT_NEAR(expected, value->ToFloat(isolate()), precision)
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_NEAR(expected, fxv8::ReentrantToFloatHelper(isolate(), value),
+                precision)
         << "Program: " << input;
   }
 
   void ExecuteExpectString(ByteStringView input, const char* expected) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
-    CFXJSE_Value* value = GetValue();
-    EXPECT_TRUE(value->IsString(isolate())) << "Program: " << input;
-    EXPECT_STREQ(expected, value->ToString(isolate()).c_str())
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsString(value));
+    EXPECT_STREQ(expected,
+                 fxv8::ReentrantToByteStringHelper(isolate(), value).c_str())
         << "Program: " << input;
   }
 };
@@ -1103,8 +1114,9 @@
   ASSERT_TRUE(OpenDocument("simple_xfa.pdf"));
   EXPECT_TRUE(Execute("Uuid()"));
 
-  CFXJSE_Value* value = GetValue();
-  EXPECT_TRUE(value->IsString(isolate()));
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+  v8::Local<v8::Value> value = GetValue();
+  EXPECT_TRUE(fxv8::IsString(value));
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Upper) {
diff --git a/testing/xfa_js_embedder_test.cpp b/testing/xfa_js_embedder_test.cpp
index c1a9b13..80defc8 100644
--- a/testing/xfa_js_embedder_test.cpp
+++ b/testing/xfa_js_embedder_test.cpp
@@ -8,7 +8,9 @@
 
 #include "fpdfsdk/cpdfsdk_helpers.h"
 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,7 +23,7 @@
 }
 
 void XFAJSEmbedderTest::TearDown() {
-  value_.reset();
+  value_.Reset();
   script_context_ = nullptr;
 
   JSEmbedderTest::TearDown();
@@ -39,6 +41,10 @@
   return pContext->GetXFADoc()->GetXFADoc();
 }
 
+v8::Local<v8::Value> XFAJSEmbedderTest::GetValue() const {
+  return v8::Local<v8::Value>::New(isolate(), value_);
+}
+
 bool XFAJSEmbedderTest::OpenDocumentWithOptions(
     const std::string& filename,
     const char* password,
@@ -55,27 +61,38 @@
 }
 
 bool XFAJSEmbedderTest::Execute(ByteStringView input) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(script_context_->GetJseContext());
   if (ExecuteHelper(input))
     return true;
 
-  CFXJSE_Value msg;
-  value_->GetObjectPropertyByIdx(isolate(), 1, &msg);
   fprintf(stderr, "FormCalc: %.*s\n", static_cast<int>(input.GetLength()),
           input.unterminated_c_str());
-  // If the parsing of the input fails, then v8 will not run, so there will be
-  // no value here to print.
-  if (msg.IsString(isolate()) && !msg.ToWideString(isolate()).IsEmpty())
-    fprintf(stderr, "JS ERROR: %ls\n", msg.ToWideString(isolate()).c_str());
+
+  v8::Local<v8::Value> result = GetValue();
+  if (!fxv8::IsArray(result))
+    return false;
+
+  v8::Local<v8::Value> msg = fxv8::ReentrantGetArrayElementHelper(
+      isolate(), result.As<v8::Array>(), 1);
+  if (!fxv8::IsString(msg))
+    return false;
+
+  WideString str = fxv8::ReentrantToWideStringHelper(isolate(), msg);
+  if (!str.IsEmpty())
+    fprintf(stderr, "JS ERROR: %ls\n", str.c_str());
   return false;
 }
 
 bool XFAJSEmbedderTest::ExecuteSilenceFailure(ByteStringView input) {
+  CFXJSE_ScopeUtil_IsolateHandleContext scope(script_context_->GetJseContext());
   return ExecuteHelper(input);
 }
 
 bool XFAJSEmbedderTest::ExecuteHelper(ByteStringView input) {
-  value_ = std::make_unique<CFXJSE_Value>();
-  return script_context_->RunScript(CXFA_Script::Type::Formcalc,
-                                    WideString::FromUTF8(input).AsStringView(),
-                                    value_.get(), GetXFADocument()->GetRoot());
+  auto value = std::make_unique<CFXJSE_Value>();
+  bool ret = script_context_->RunScript(
+      CXFA_Script::Type::Formcalc, WideString::FromUTF8(input).AsStringView(),
+      value.get(), GetXFADocument()->GetRoot());
+  value_.Reset(isolate(), value->GetValue(isolate()));
+  return ret;
 }
diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h
index 0e47f34..3d223f7 100644
--- a/testing/xfa_js_embedder_test.h
+++ b/testing/xfa_js_embedder_test.h
@@ -10,9 +10,9 @@
 
 #include "core/fxcrt/string_view_template.h"
 #include "testing/js_embedder_test.h"
+#include "v8/include/v8.h"
 
 class CFXJSE_Engine;
-class CFXJSE_Value;
 class CXFA_Document;
 
 class XFAJSEmbedderTest : public JSEmbedderTest {
@@ -30,7 +30,7 @@
 
   CXFA_Document* GetXFADocument() const;
   CFXJSE_Engine* GetScriptContext() const { return script_context_; }
-  CFXJSE_Value* GetValue() const { return value_.get(); }
+  v8::Local<v8::Value> GetValue() const;
 
   bool Execute(ByteStringView input);
   bool ExecuteSilenceFailure(ByteStringView input);
@@ -38,7 +38,7 @@
  private:
   bool ExecuteHelper(ByteStringView input);
 
-  std::unique_ptr<CFXJSE_Value> value_;
+  v8::Global<v8::Value> value_;
   CFXJSE_Engine* script_context_ = nullptr;
 };