Remove CFXJSE_Values from CFXJSE_FormCalcContext::Mod() and Round()

-- Return Optional<double> from ExtractDouble().
-- Add test for Mod(NaN).

Change-Id: I59e4e0eca0d8c7e315847b3a9132a8c922ecfad8
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/76294
Reviewed-by: Daniel Hosseinian <dhoss@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/fxjs/xfa/cfxjse_formcalc_context.cpp b/fxjs/xfa/cfxjse_formcalc_context.cpp
index 2d99496..c0193dd 100644
--- a/fxjs/xfa/cfxjse_formcalc_context.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context.cpp
@@ -21,6 +21,7 @@
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_value.h"
 #include "fxjs/xfa/cjx_object.h"
+#include "third_party/base/optional.h"
 #include "third_party/base/stl_util.h"
 #include "xfa/fgas/crt/cfgas_decimal.h"
 #include "xfa/fxfa/cxfa_ffnotify.h"
@@ -1446,25 +1447,18 @@
   return fxv8::ReentrantToDoubleHelper(pIsolate, extracted);
 }
 
-// TODO(tsepez): return Optional<double>.
-double ExtractDouble(v8::Isolate* pIsolate,
-                     v8::Local<v8::Value> src,
-                     bool* ret) {
-  ASSERT(ret);
-  *ret = true;
-
+Optional<double> ExtractDouble(v8::Isolate* pIsolate,
+                               v8::Local<v8::Value> src) {
   if (src.IsEmpty())
-    return 0;
+    return 0.0;
 
   if (!fxv8::IsArray(src))
     return ValueToDouble(pIsolate, src);
 
   v8::Local<v8::Array> arr = src.As<v8::Array>();
   uint32_t iLength = fxv8::GetArrayLengthHelper(arr);
-  if (iLength < 3) {
-    *ret = false;
-    return 0.0;
-  }
+  if (iLength < 3)
+    return pdfium::nullopt;
 
   v8::Local<v8::Value> propertyValue =
       fxv8::ReentrantGetArrayElementHelper(pIsolate, arr, 1);
@@ -1852,31 +1846,27 @@
     return;
   }
 
-  auto argOne = std::make_unique<CFXJSE_Value>(info.GetIsolate(), info[0]);
-  auto argTwo = std::make_unique<CFXJSE_Value>(info.GetIsolate(), info[1]);
-  if (argOne->IsNull(info.GetIsolate()) || argTwo->IsNull(info.GetIsolate())) {
+  if (fxv8::IsNull(info[0]) || fxv8::IsNull(info[1])) {
     info.GetReturnValue().SetNull();
     return;
   }
 
-  bool argOneResult;
-  double dDividend = ExtractDouble(
-      info.GetIsolate(), argOne->GetValue(info.GetIsolate()), &argOneResult);
-  bool argTwoResult;
-  double dDivisor = ExtractDouble(
-      info.GetIsolate(), argTwo->GetValue(info.GetIsolate()), &argTwoResult);
-  if (!argOneResult || !argTwoResult) {
+  Optional<double> maybe_dividend = ExtractDouble(info.GetIsolate(), info[0]);
+  Optional<double> maybe_divisor = ExtractDouble(info.GetIsolate(), info[1]);
+  if (!maybe_dividend.has_value() || !maybe_divisor.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
-  if (dDivisor == 0.0) {
+  double dividend = maybe_dividend.value();
+  double divisor = maybe_divisor.value();
+  if (divisor == 0.0) {
     pContext->ThrowDivideByZeroException();
     return;
   }
 
-  info.GetReturnValue().Set(
-      dDividend - dDivisor * static_cast<int32_t>(dDividend / dDivisor));
+  info.GetReturnValue().Set(dividend -
+                            divisor * static_cast<int32_t>(dividend / divisor));
 }
 
 // static
@@ -1890,36 +1880,31 @@
     return;
   }
 
-  auto argOne = std::make_unique<CFXJSE_Value>(info.GetIsolate(), info[0]);
-  if (argOne->IsNull(info.GetIsolate())) {
+  if (fxv8::IsNull(info[0])) {
     info.GetReturnValue().SetNull();
     return;
   }
 
-  bool dValueRet;
-  double dValue = ExtractDouble(
-      info.GetIsolate(), argOne->GetValue(info.GetIsolate()), &dValueRet);
-  if (!dValueRet) {
+  Optional<double> maybe_value = ExtractDouble(info.GetIsolate(), info[0]);
+  if (!maybe_value.has_value()) {
     pContext->ThrowArgumentMismatchException();
     return;
   }
 
+  double dValue = maybe_value.value();
   uint8_t uPrecision = 0;
   if (argc > 1) {
-    auto argTwo = std::make_unique<CFXJSE_Value>(info.GetIsolate(), info[1]);
-    if (argTwo->IsNull(info.GetIsolate())) {
+    if (fxv8::IsNull(info[1])) {
       info.GetReturnValue().SetNull();
       return;
     }
-
-    bool dPrecisionRet;
-    double dPrecision = ExtractDouble(
-        info.GetIsolate(), argTwo->GetValue(info.GetIsolate()), &dPrecisionRet);
-    if (!dPrecisionRet) {
+    Optional<double> maybe_precision =
+        ExtractDouble(info.GetIsolate(), info[1]);
+    if (!maybe_precision.has_value()) {
       pContext->ThrowArgumentMismatchException();
       return;
     }
-
+    double dPrecision = maybe_precision.value();
     uPrecision = static_cast<uint8_t>(pdfium::clamp(dPrecision, 0.0, 12.0));
   }
 
diff --git a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
index ad523a6..b2cd1d2 100644
--- a/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
+++ b/fxjs/xfa/cfxjse_formcalc_context_embeddertest.cpp
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <cmath>
+
 #include "fxjs/fxv8.h"
 #include "fxjs/xfa/cfxjse_engine.h"
 #include "fxjs/xfa/cfxjse_isolatetracker.h"
@@ -74,6 +76,15 @@
         << "Program: " << input;
   }
 
+  void ExecuteExpectNaN(ByteStringView input) {
+    EXPECT_TRUE(Execute(input)) << "Program: " << input;
+
+    CFXJSE_ScopeUtil_IsolateHandleContext scope(GetScriptContext());
+    v8::Local<v8::Value> value = GetValue();
+    EXPECT_TRUE(fxv8::IsNumber(value));
+    EXPECT_TRUE(std::isnan(fxv8::ReentrantToDoubleHelper(isolate(), value)));
+  }
+
   void ExecuteExpectString(ByteStringView input, const char* expected) {
     EXPECT_TRUE(Execute(input)) << "Program: " << input;
 
@@ -288,6 +299,9 @@
 
   for (size_t i = 0; i < pdfium::size(tests); ++i)
     ExecuteExpectInt32(tests[i].program, tests[i].result);
+
+  ExecuteExpectNaN("Mod(10, NaN)");
+  ExecuteExpectNaN("Mod(10, Infinity)");
 }
 
 TEST_F(CFXJSE_FormCalcContextEmbedderTest, Round) {