[gjs: 2/3] value: Fix regression converting Infinity and NaN to C integers




commit 4d8e71c6a651f484fa07857852aa41eeb4131c80
Author: Philip Chimento <philip chimento gmail com>
Date:   Wed Feb 9 22:15:14 2022 -0800

    value: Fix regression converting Infinity and NaN to C integers
    
    This regression has been around somewhat longer, but JS::ToInt32,
    JS::ToInt64, and friends all treat Infinity and NaN as 0 when converting
    to APIs that expect an integer. GJS used to do this as well. Add tests so
    it doesn't regress again.
    
    Additionally add tests for float and double, to make sure that Infinity
    and NaN are preserved when converting to C float and double.

 gi/js-value-inl.h                 | 17 ++++++++++++++++-
 installed-tests/js/testRegress.js | 25 +++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)
---
diff --git a/gi/js-value-inl.h b/gi/js-value-inl.h
index fe5be3c0f..debfe1527 100644
--- a/gi/js-value-inl.h
+++ b/gi/js-value-inl.h
@@ -248,6 +248,10 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
                 bi = value.toBigInt();
             } else if (value.isNumber()) {
                 double number = value.toNumber();
+                if (!std::isfinite(number)) {
+                    *out = 0;
+                    return true;
+                }
                 number = std::trunc(number);
                 bi = JS::NumberToBigInt(cx, number);
                 if (!bi)
@@ -264,8 +268,10 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
     if constexpr (std::is_same_v<WantedType, T>)
         return js_value_to_c(cx, value, out);
 
+    // JS::ToIntNN() converts undefined, NaN, infinity to 0
     if constexpr (std::is_integral_v<WantedType>) {
-        if (value.isUndefined()) {
+        if (value.isUndefined() ||
+            (value.isDouble() && !std::isfinite(value.toDouble()))) {
             *out = 0;
             return true;
         }
@@ -274,6 +280,15 @@ GJS_JSAPI_RETURN_CONVENTION inline bool js_value_to_c_checked(
     if constexpr (std::is_arithmetic_v<T>) {
         bool ret = js_value_to_c(cx, value, out);
         if (out_of_range) {
+            // Infinity and NaN preserved between floating point types
+            if constexpr (std::is_floating_point_v<WantedType> &&
+                          std::is_floating_point_v<T>) {
+                if (!std::isfinite(*out)) {
+                    *out_of_range = false;
+                    return ret;
+                }
+            }
+
             *out_of_range =
                 (*out >
                      static_cast<T>(std::numeric_limits<WantedType>::max()) ||
diff --git a/installed-tests/js/testRegress.js b/installed-tests/js/testRegress.js
index f5935300a..36d062b28 100644
--- a/installed-tests/js/testRegress.js
+++ b/installed-tests/js/testRegress.js
@@ -124,6 +124,31 @@ describe('Life, the Universe and Everything', function () {
         });
     });
 
+    describe('Infinity and NaN', function () {
+        ['int8', 'int16', 'int32', 'int64', 'short', 'int', 'long', 'ssize'].forEach(type => {
+            it(`converts to 0 for ${type}`, function () {
+                expect(Regress[`test_${type}`](Infinity)).toBe(0);
+                expect(Regress[`test_${type}`](-Infinity)).toBe(0);
+                expect(Regress[`test_${type}`](NaN)).toBe(0);
+            });
+        });
+
+        ['uint8', 'uint16', 'uint32', 'uint64', 'ushort', 'uint', 'ulong', 'size'].forEach(type => {
+            it(`converts to 0 for ${type}`, function () {
+                expect(Regress[`test_${type}`](Infinity)).toBe(0);
+                expect(Regress[`test_${type}`](NaN)).toBe(0);
+            });
+        });
+
+        ['float', 'double'].forEach(type => {
+            it(`not for ${type}`, function () {
+                expect(Regress[`test_${type}`](Infinity)).toBe(Infinity);
+                expect(Regress[`test_${type}`](-Infinity)).toBe(-Infinity);
+                expect(Regress[`test_${type}`](NaN)).toBeNaN();
+            });
+        });
+    });
+
     describe('(u)int64 numeric values', function () {
         const minInt64 = -(2n ** 63n);
         const maxInt64 = 2n ** 63n - 1n;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]